summaryrefslogtreecommitdiff
path: root/spec/rubyspec/core
diff options
context:
space:
mode:
Diffstat (limited to 'spec/rubyspec/core')
-rw-r--r--spec/rubyspec/core/argf/argf_spec.rb11
-rw-r--r--spec/rubyspec/core/argf/argv_spec.rb19
-rw-r--r--spec/rubyspec/core/argf/binmode_spec.rb46
-rw-r--r--spec/rubyspec/core/argf/bytes_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/chars_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/close_spec.rb46
-rw-r--r--spec/rubyspec/core/argf/closed_spec.rb18
-rw-r--r--spec/rubyspec/core/argf/codepoints_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/each_byte_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/each_char_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/each_codepoint_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/each_line_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/each_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/eof_spec.rb10
-rw-r--r--spec/rubyspec/core/argf/file_spec.rb21
-rw-r--r--spec/rubyspec/core/argf/filename_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/fileno_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/fixtures/bin_file.txt2
-rw-r--r--spec/rubyspec/core/argf/fixtures/encoding.rb5
-rw-r--r--spec/rubyspec/core/argf/fixtures/file1.txt2
-rw-r--r--spec/rubyspec/core/argf/fixtures/file2.txt2
-rw-r--r--spec/rubyspec/core/argf/fixtures/filename.rb3
-rw-r--r--spec/rubyspec/core/argf/fixtures/lineno.rb5
-rw-r--r--spec/rubyspec/core/argf/fixtures/rewind.rb5
-rw-r--r--spec/rubyspec/core/argf/fixtures/stdin.txt2
-rw-r--r--spec/rubyspec/core/argf/getc_spec.rb20
-rw-r--r--spec/rubyspec/core/argf/gets_spec.rb51
-rw-r--r--spec/rubyspec/core/argf/lineno_spec.rb30
-rw-r--r--spec/rubyspec/core/argf/lines_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/path_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/pos_spec.rb38
-rw-r--r--spec/rubyspec/core/argf/read_nonblock_spec.rb82
-rw-r--r--spec/rubyspec/core/argf/read_spec.rb87
-rw-r--r--spec/rubyspec/core/argf/readchar_spec.rb19
-rw-r--r--spec/rubyspec/core/argf/readline_spec.rb23
-rw-r--r--spec/rubyspec/core/argf/readlines_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/readpartial_spec.rb77
-rw-r--r--spec/rubyspec/core/argf/rewind_spec.rb39
-rw-r--r--spec/rubyspec/core/argf/seek_spec.rb63
-rw-r--r--spec/rubyspec/core/argf/set_encoding_spec.rb28
-rw-r--r--spec/rubyspec/core/argf/shared/each_byte.rb58
-rw-r--r--spec/rubyspec/core/argf/shared/each_char.rb58
-rw-r--r--spec/rubyspec/core/argf/shared/each_codepoint.rb58
-rw-r--r--spec/rubyspec/core/argf/shared/each_line.rb62
-rw-r--r--spec/rubyspec/core/argf/shared/eof.rb24
-rw-r--r--spec/rubyspec/core/argf/shared/filename.rb28
-rw-r--r--spec/rubyspec/core/argf/shared/fileno.rb24
-rw-r--r--spec/rubyspec/core/argf/shared/getc.rb17
-rw-r--r--spec/rubyspec/core/argf/shared/gets.rb99
-rw-r--r--spec/rubyspec/core/argf/shared/pos.rb31
-rw-r--r--spec/rubyspec/core/argf/shared/read.rb58
-rw-r--r--spec/rubyspec/core/argf/shared/readlines.rb22
-rw-r--r--spec/rubyspec/core/argf/skip_spec.rb42
-rw-r--r--spec/rubyspec/core/argf/tell_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/to_a_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/to_i_spec.rb6
-rw-r--r--spec/rubyspec/core/argf/to_io_spec.rb23
-rw-r--r--spec/rubyspec/core/argf/to_s_spec.rb14
-rw-r--r--spec/rubyspec/core/array/allocate_spec.rb19
-rw-r--r--spec/rubyspec/core/array/any_spec.rb37
-rw-r--r--spec/rubyspec/core/array/append_spec.rb35
-rw-r--r--spec/rubyspec/core/array/array_spec.rb7
-rw-r--r--spec/rubyspec/core/array/assoc_spec.rb40
-rw-r--r--spec/rubyspec/core/array/at_spec.rb56
-rw-r--r--spec/rubyspec/core/array/bsearch_index_spec.rb87
-rw-r--r--spec/rubyspec/core/array/bsearch_spec.rb84
-rw-r--r--spec/rubyspec/core/array/clear_spec.rb49
-rw-r--r--spec/rubyspec/core/array/clone_spec.rb31
-rw-r--r--spec/rubyspec/core/array/collect_spec.rb11
-rw-r--r--spec/rubyspec/core/array/combination_spec.rb74
-rw-r--r--spec/rubyspec/core/array/compact_spec.rb77
-rw-r--r--spec/rubyspec/core/array/comparison_spec.rb97
-rw-r--r--spec/rubyspec/core/array/concat_spec.rb132
-rw-r--r--spec/rubyspec/core/array/constructor_spec.rb24
-rw-r--r--spec/rubyspec/core/array/count_spec.rb15
-rw-r--r--spec/rubyspec/core/array/cycle_spec.rb101
-rw-r--r--spec/rubyspec/core/array/delete_at_spec.rb61
-rw-r--r--spec/rubyspec/core/array/delete_if_spec.rb66
-rw-r--r--spec/rubyspec/core/array/delete_spec.rb66
-rw-r--r--spec/rubyspec/core/array/dig_spec.rb54
-rw-r--r--spec/rubyspec/core/array/drop_spec.rb33
-rw-r--r--spec/rubyspec/core/array/drop_while_spec.rb15
-rw-r--r--spec/rubyspec/core/array/dup_spec.rb31
-rw-r--r--spec/rubyspec/core/array/each_index_spec.rb42
-rw-r--r--spec/rubyspec/core/array/each_spec.rb32
-rw-r--r--spec/rubyspec/core/array/element_reference_spec.rb50
-rw-r--r--spec/rubyspec/core/array/element_set_spec.rb418
-rw-r--r--spec/rubyspec/core/array/empty_spec.rb10
-rw-r--r--spec/rubyspec/core/array/eql_spec.rb19
-rw-r--r--spec/rubyspec/core/array/equal_value_spec.rb51
-rw-r--r--spec/rubyspec/core/array/fetch_spec.rb55
-rw-r--r--spec/rubyspec/core/array/fill_spec.rb317
-rw-r--r--spec/rubyspec/core/array/find_index_spec.rb6
-rw-r--r--spec/rubyspec/core/array/first_spec.rb93
-rw-r--r--spec/rubyspec/core/array/fixtures/classes.rb525
-rw-r--r--spec/rubyspec/core/array/fixtures/encoded_strings.rb69
-rw-r--r--spec/rubyspec/core/array/flatten_spec.rb270
-rw-r--r--spec/rubyspec/core/array/frozen_spec.rb16
-rw-r--r--spec/rubyspec/core/array/hash_spec.rb83
-rw-r--r--spec/rubyspec/core/array/include_spec.rb33
-rw-r--r--spec/rubyspec/core/array/index_spec.rb6
-rw-r--r--spec/rubyspec/core/array/initialize_spec.rb156
-rw-r--r--spec/rubyspec/core/array/insert_spec.rb78
-rw-r--r--spec/rubyspec/core/array/inspect_spec.rb7
-rw-r--r--spec/rubyspec/core/array/intersection_spec.rb86
-rw-r--r--spec/rubyspec/core/array/join_spec.rb48
-rw-r--r--spec/rubyspec/core/array/keep_if_spec.rb10
-rw-r--r--spec/rubyspec/core/array/last_spec.rb87
-rw-r--r--spec/rubyspec/core/array/length_spec.rb7
-rw-r--r--spec/rubyspec/core/array/map_spec.rb11
-rw-r--r--spec/rubyspec/core/array/max_spec.rb112
-rw-r--r--spec/rubyspec/core/array/min_spec.rb117
-rw-r--r--spec/rubyspec/core/array/minus_spec.rb87
-rw-r--r--spec/rubyspec/core/array/multiply_spec.rb132
-rw-r--r--spec/rubyspec/core/array/new_spec.rb122
-rw-r--r--spec/rubyspec/core/array/pack/a_spec.rb59
-rw-r--r--spec/rubyspec/core/array/pack/at_spec.rb30
-rw-r--r--spec/rubyspec/core/array/pack/b_spec.rb105
-rw-r--r--spec/rubyspec/core/array/pack/c_spec.rb75
-rw-r--r--spec/rubyspec/core/array/pack/comment_spec.rb25
-rw-r--r--spec/rubyspec/core/array/pack/d_spec.rb39
-rw-r--r--spec/rubyspec/core/array/pack/e_spec.rb25
-rw-r--r--spec/rubyspec/core/array/pack/empty_spec.rb11
-rw-r--r--spec/rubyspec/core/array/pack/f_spec.rb39
-rw-r--r--spec/rubyspec/core/array/pack/g_spec.rb25
-rw-r--r--spec/rubyspec/core/array/pack/h_spec.rb197
-rw-r--r--spec/rubyspec/core/array/pack/i_spec.rb133
-rw-r--r--spec/rubyspec/core/array/pack/j_spec.rb222
-rw-r--r--spec/rubyspec/core/array/pack/l_spec.rb309
-rw-r--r--spec/rubyspec/core/array/pack/m_spec.rb306
-rw-r--r--spec/rubyspec/core/array/pack/n_spec.rb25
-rw-r--r--spec/rubyspec/core/array/pack/p_spec.rb11
-rw-r--r--spec/rubyspec/core/array/pack/percent_spec.rb7
-rw-r--r--spec/rubyspec/core/array/pack/q_spec.rb61
-rw-r--r--spec/rubyspec/core/array/pack/s_spec.rb133
-rw-r--r--spec/rubyspec/core/array/pack/shared/basic.rb65
-rw-r--r--spec/rubyspec/core/array/pack/shared/encodings.rb16
-rw-r--r--spec/rubyspec/core/array/pack/shared/float.rb249
-rw-r--r--spec/rubyspec/core/array/pack/shared/integer.rb381
-rw-r--r--spec/rubyspec/core/array/pack/shared/numeric_basic.rb44
-rw-r--r--spec/rubyspec/core/array/pack/shared/string.rb80
-rw-r--r--spec/rubyspec/core/array/pack/shared/unicode.rb94
-rw-r--r--spec/rubyspec/core/array/pack/u_spec.rb128
-rw-r--r--spec/rubyspec/core/array/pack/v_spec.rb25
-rw-r--r--spec/rubyspec/core/array/pack/w_spec.rb42
-rw-r--r--spec/rubyspec/core/array/pack/x_spec.rb64
-rw-r--r--spec/rubyspec/core/array/pack/z_spec.rb32
-rw-r--r--spec/rubyspec/core/array/partition_spec.rb43
-rw-r--r--spec/rubyspec/core/array/permutation_spec.rb138
-rw-r--r--spec/rubyspec/core/array/plus_spec.rb57
-rw-r--r--spec/rubyspec/core/array/pop_spec.rb168
-rw-r--r--spec/rubyspec/core/array/product_spec.rb68
-rw-r--r--spec/rubyspec/core/array/push_spec.rb36
-rw-r--r--spec/rubyspec/core/array/rassoc_spec.rb38
-rw-r--r--spec/rubyspec/core/array/reject_spec.rb117
-rw-r--r--spec/rubyspec/core/array/repeated_combination_spec.rb84
-rw-r--r--spec/rubyspec/core/array/repeated_permutation_spec.rb94
-rw-r--r--spec/rubyspec/core/array/replace_spec.rb7
-rw-r--r--spec/rubyspec/core/array/reverse_each_spec.rb43
-rw-r--r--spec/rubyspec/core/array/reverse_spec.rb42
-rw-r--r--spec/rubyspec/core/array/rindex_spec.rb80
-rw-r--r--spec/rubyspec/core/array/rotate_spec.rb129
-rw-r--r--spec/rubyspec/core/array/sample_spec.rb155
-rw-r--r--spec/rubyspec/core/array/select_spec.rb36
-rw-r--r--spec/rubyspec/core/array/shared/clone.rb42
-rw-r--r--spec/rubyspec/core/array/shared/collect.rb136
-rw-r--r--spec/rubyspec/core/array/shared/delete_if.rb27
-rw-r--r--spec/rubyspec/core/array/shared/enumeratorize.rb5
-rw-r--r--spec/rubyspec/core/array/shared/eql.rb92
-rw-r--r--spec/rubyspec/core/array/shared/index.rb37
-rw-r--r--spec/rubyspec/core/array/shared/inspect.rb104
-rw-r--r--spec/rubyspec/core/array/shared/join.rb161
-rw-r--r--spec/rubyspec/core/array/shared/keep_if.rb60
-rw-r--r--spec/rubyspec/core/array/shared/length.rb11
-rw-r--r--spec/rubyspec/core/array/shared/replace.rb60
-rw-r--r--spec/rubyspec/core/array/shared/slice.rb459
-rw-r--r--spec/rubyspec/core/array/shift_spec.rb134
-rw-r--r--spec/rubyspec/core/array/shuffle_spec.rb102
-rw-r--r--spec/rubyspec/core/array/size_spec.rb7
-rw-r--r--spec/rubyspec/core/array/slice_spec.rb160
-rw-r--r--spec/rubyspec/core/array/sort_by_spec.rb52
-rw-r--r--spec/rubyspec/core/array/sort_spec.rb250
-rw-r--r--spec/rubyspec/core/array/take_spec.rb27
-rw-r--r--spec/rubyspec/core/array/take_while_spec.rb15
-rw-r--r--spec/rubyspec/core/array/to_a_spec.rb24
-rw-r--r--spec/rubyspec/core/array/to_ary_spec.rb20
-rw-r--r--spec/rubyspec/core/array/to_h_spec.rb37
-rw-r--r--spec/rubyspec/core/array/to_s_spec.rb8
-rw-r--r--spec/rubyspec/core/array/transpose_spec.rb53
-rw-r--r--spec/rubyspec/core/array/try_convert_spec.rb50
-rw-r--r--spec/rubyspec/core/array/union_spec.rb82
-rw-r--r--spec/rubyspec/core/array/uniq_spec.rb221
-rw-r--r--spec/rubyspec/core/array/unshift_spec.rb50
-rw-r--r--spec/rubyspec/core/array/values_at_spec.rb63
-rw-r--r--spec/rubyspec/core/array/zip_spec.rb65
-rw-r--r--spec/rubyspec/core/basicobject/__id__spec.rb6
-rw-r--r--spec/rubyspec/core/basicobject/__send___spec.rb10
-rw-r--r--spec/rubyspec/core/basicobject/basicobject_spec.rb87
-rw-r--r--spec/rubyspec/core/basicobject/equal_spec.rb52
-rw-r--r--spec/rubyspec/core/basicobject/equal_value_spec.rb10
-rw-r--r--spec/rubyspec/core/basicobject/fixtures/classes.rb33
-rw-r--r--spec/rubyspec/core/basicobject/fixtures/common.rb9
-rw-r--r--spec/rubyspec/core/basicobject/fixtures/remove_method_missing.rb9
-rw-r--r--spec/rubyspec/core/basicobject/fixtures/singleton_method.rb10
-rw-r--r--spec/rubyspec/core/basicobject/initialize_spec.rb13
-rw-r--r--spec/rubyspec/core/basicobject/instance_eval_spec.rb180
-rw-r--r--spec/rubyspec/core/basicobject/instance_exec_spec.rb107
-rw-r--r--spec/rubyspec/core/basicobject/method_missing_spec.rb39
-rw-r--r--spec/rubyspec/core/basicobject/not_equal_spec.rb53
-rw-r--r--spec/rubyspec/core/basicobject/not_spec.rb11
-rw-r--r--spec/rubyspec/core/basicobject/singleton_method_added_spec.rb86
-rw-r--r--spec/rubyspec/core/basicobject/singleton_method_removed_spec.rb24
-rw-r--r--spec/rubyspec/core/basicobject/singleton_method_undefined_spec.rb24
-rw-r--r--spec/rubyspec/core/bignum/abs_spec.rb7
-rw-r--r--spec/rubyspec/core/bignum/bignum_spec.rb7
-rw-r--r--spec/rubyspec/core/bignum/bit_and_spec.rb50
-rw-r--r--spec/rubyspec/core/bignum/bit_length_spec.rb33
-rw-r--r--spec/rubyspec/core/bignum/bit_or_spec.rb41
-rw-r--r--spec/rubyspec/core/bignum/bit_xor_spec.rb47
-rw-r--r--spec/rubyspec/core/bignum/case_compare_spec.rb6
-rw-r--r--spec/rubyspec/core/bignum/coerce_spec.rb65
-rw-r--r--spec/rubyspec/core/bignum/comparison_spec.rb162
-rw-r--r--spec/rubyspec/core/bignum/complement_spec.rb9
-rw-r--r--spec/rubyspec/core/bignum/div_spec.rb21
-rw-r--r--spec/rubyspec/core/bignum/divide_spec.rb18
-rw-r--r--spec/rubyspec/core/bignum/divmod_spec.rb81
-rw-r--r--spec/rubyspec/core/bignum/element_reference_spec.rb30
-rw-r--r--spec/rubyspec/core/bignum/eql_spec.rb22
-rw-r--r--spec/rubyspec/core/bignum/equal_value_spec.rb6
-rw-r--r--spec/rubyspec/core/bignum/even_spec.rb19
-rw-r--r--spec/rubyspec/core/bignum/exponent_spec.rb29
-rw-r--r--spec/rubyspec/core/bignum/fdiv_spec.rb5
-rw-r--r--spec/rubyspec/core/bignum/gt_spec.rb20
-rw-r--r--spec/rubyspec/core/bignum/gte_spec.rb19
-rw-r--r--spec/rubyspec/core/bignum/hash_spec.rb12
-rw-r--r--spec/rubyspec/core/bignum/left_shift_spec.rb73
-rw-r--r--spec/rubyspec/core/bignum/lt_spec.rb22
-rw-r--r--spec/rubyspec/core/bignum/lte_spec.rb24
-rw-r--r--spec/rubyspec/core/bignum/magnitude_spec.rb6
-rw-r--r--spec/rubyspec/core/bignum/minus_spec.rb19
-rw-r--r--spec/rubyspec/core/bignum/modulo_spec.rb10
-rw-r--r--spec/rubyspec/core/bignum/multiply_spec.rb20
-rw-r--r--spec/rubyspec/core/bignum/odd_spec.rb19
-rw-r--r--spec/rubyspec/core/bignum/plus_spec.rb19
-rw-r--r--spec/rubyspec/core/bignum/remainder_spec.rb21
-rw-r--r--spec/rubyspec/core/bignum/right_shift_spec.rb99
-rw-r--r--spec/rubyspec/core/bignum/shared/abs.rb6
-rw-r--r--spec/rubyspec/core/bignum/shared/divide.rb27
-rw-r--r--spec/rubyspec/core/bignum/shared/equal.rb31
-rw-r--r--spec/rubyspec/core/bignum/shared/modulo.rb29
-rw-r--r--spec/rubyspec/core/bignum/size_spec.rb16
-rw-r--r--spec/rubyspec/core/bignum/to_f_spec.rb13
-rw-r--r--spec/rubyspec/core/bignum/to_s_spec.rb48
-rw-r--r--spec/rubyspec/core/bignum/uminus_spec.rb11
-rw-r--r--spec/rubyspec/core/binding/clone_spec.rb7
-rw-r--r--spec/rubyspec/core/binding/dup_spec.rb7
-rw-r--r--spec/rubyspec/core/binding/eval_spec.rb27
-rw-r--r--spec/rubyspec/core/binding/fixtures/classes.rb32
-rw-r--r--spec/rubyspec/core/binding/local_variable_defined_spec.rb46
-rw-r--r--spec/rubyspec/core/binding/local_variable_get_spec.rb45
-rw-r--r--spec/rubyspec/core/binding/local_variable_set_spec.rb59
-rw-r--r--spec/rubyspec/core/binding/local_variables_spec.rb35
-rw-r--r--spec/rubyspec/core/binding/location_spec.rb46
-rw-r--r--spec/rubyspec/core/binding/receiver_spec.rb11
-rw-r--r--spec/rubyspec/core/binding/shared/clone.rb20
-rw-r--r--spec/rubyspec/core/builtin_constants/builtin_constants_spec.rb49
-rw-r--r--spec/rubyspec/core/class/allocate_spec.rb41
-rw-r--r--spec/rubyspec/core/class/dup_spec.rb64
-rw-r--r--spec/rubyspec/core/class/fixtures/classes.rb47
-rw-r--r--spec/rubyspec/core/class/inherited_spec.rb102
-rw-r--r--spec/rubyspec/core/class/initialize_spec.rb34
-rw-r--r--spec/rubyspec/core/class/new_spec.rb154
-rw-r--r--spec/rubyspec/core/class/superclass_spec.rb27
-rw-r--r--spec/rubyspec/core/class/to_s_spec.rb23
-rw-r--r--spec/rubyspec/core/comparable/between_spec.rb25
-rw-r--r--spec/rubyspec/core/comparable/clamp_spec.rb50
-rw-r--r--spec/rubyspec/core/comparable/equal_value_spec.rb139
-rw-r--r--spec/rubyspec/core/comparable/fixtures/classes.rb36
-rw-r--r--spec/rubyspec/core/comparable/gt_spec.rb43
-rw-r--r--spec/rubyspec/core/comparable/gte_spec.rb47
-rw-r--r--spec/rubyspec/core/comparable/lt_spec.rb43
-rw-r--r--spec/rubyspec/core/comparable/lte_spec.rb46
-rw-r--r--spec/rubyspec/core/complex/abs2_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/abs_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/angle_spec.rb7
-rw-r--r--spec/rubyspec/core/complex/arg_spec.rb7
-rw-r--r--spec/rubyspec/core/complex/coerce_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/conj_spec.rb6
-rw-r--r--spec/rubyspec/core/complex/conjugate_spec.rb6
-rw-r--r--spec/rubyspec/core/complex/constants_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/denominator_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/divide_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/eql_spec.rb31
-rw-r--r--spec/rubyspec/core/complex/equal_value_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/exponent_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/fdiv_spec.rb129
-rw-r--r--spec/rubyspec/core/complex/hash_spec.rb6
-rw-r--r--spec/rubyspec/core/complex/imag_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/imaginary_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/inspect_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/integer_spec.rb9
-rw-r--r--spec/rubyspec/core/complex/magnitude_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/marshal_dump_spec.rb11
-rw-r--r--spec/rubyspec/core/complex/minus_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/multiply_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/negative_spec.rb11
-rw-r--r--spec/rubyspec/core/complex/numerator_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/phase_spec.rb6
-rw-r--r--spec/rubyspec/core/complex/plus_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/polar_spec.rb14
-rw-r--r--spec/rubyspec/core/complex/positive_spec.rb11
-rw-r--r--spec/rubyspec/core/complex/quo_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/rationalize_spec.rb29
-rw-r--r--spec/rubyspec/core/complex/real_spec.rb23
-rw-r--r--spec/rubyspec/core/complex/rect_spec.rb9
-rw-r--r--spec/rubyspec/core/complex/rectangular_spec.rb9
-rw-r--r--spec/rubyspec/core/complex/to_f_spec.rb41
-rw-r--r--spec/rubyspec/core/complex/to_i_spec.rb41
-rw-r--r--spec/rubyspec/core/complex/to_r_spec.rb41
-rw-r--r--spec/rubyspec/core/complex/to_s_spec.rb5
-rw-r--r--spec/rubyspec/core/complex/uminus_spec.rb11
-rw-r--r--spec/rubyspec/core/dir/chdir_spec.rb124
-rw-r--r--spec/rubyspec/core/dir/chroot_spec.rb47
-rw-r--r--spec/rubyspec/core/dir/close_spec.rb29
-rw-r--r--spec/rubyspec/core/dir/delete_spec.rb15
-rw-r--r--spec/rubyspec/core/dir/dir_spec.rb7
-rw-r--r--spec/rubyspec/core/dir/each_spec.rb64
-rw-r--r--spec/rubyspec/core/dir/element_reference_spec.rb33
-rw-r--r--spec/rubyspec/core/dir/entries_spec.rb70
-rw-r--r--spec/rubyspec/core/dir/exist_spec.rb15
-rw-r--r--spec/rubyspec/core/dir/exists_spec.rb15
-rw-r--r--spec/rubyspec/core/dir/fileno_spec.rb37
-rw-r--r--spec/rubyspec/core/dir/fixtures/common.rb169
-rw-r--r--spec/rubyspec/core/dir/foreach_spec.rb56
-rw-r--r--spec/rubyspec/core/dir/getwd_spec.rb15
-rw-r--r--spec/rubyspec/core/dir/glob_spec.rb156
-rw-r--r--spec/rubyspec/core/dir/home_spec.rb24
-rw-r--r--spec/rubyspec/core/dir/initialize_spec.rb23
-rw-r--r--spec/rubyspec/core/dir/inspect_spec.rb24
-rw-r--r--spec/rubyspec/core/dir/mkdir_spec.rb85
-rw-r--r--spec/rubyspec/core/dir/open_spec.rb15
-rw-r--r--spec/rubyspec/core/dir/path_spec.rb15
-rw-r--r--spec/rubyspec/core/dir/pos_spec.rb40
-rw-r--r--spec/rubyspec/core/dir/pwd_spec.rb39
-rw-r--r--spec/rubyspec/core/dir/read_spec.rb43
-rw-r--r--spec/rubyspec/core/dir/rewind_spec.rb36
-rw-r--r--spec/rubyspec/core/dir/rmdir_spec.rb15
-rw-r--r--spec/rubyspec/core/dir/seek_spec.rb19
-rw-r--r--spec/rubyspec/core/dir/shared/chroot.rb41
-rw-r--r--spec/rubyspec/core/dir/shared/closed.rb9
-rw-r--r--spec/rubyspec/core/dir/shared/delete.rb59
-rw-r--r--spec/rubyspec/core/dir/shared/exist.rb56
-rw-r--r--spec/rubyspec/core/dir/shared/glob.rb328
-rw-r--r--spec/rubyspec/core/dir/shared/open.rb63
-rw-r--r--spec/rubyspec/core/dir/shared/path.rb32
-rw-r--r--spec/rubyspec/core/dir/shared/pos.rb51
-rw-r--r--spec/rubyspec/core/dir/shared/pwd.rb49
-rw-r--r--spec/rubyspec/core/dir/tell_spec.rb18
-rw-r--r--spec/rubyspec/core/dir/to_path_spec.rb15
-rw-r--r--spec/rubyspec/core/dir/unlink_spec.rb15
-rw-r--r--spec/rubyspec/core/encoding/_dump_spec.rb5
-rw-r--r--spec/rubyspec/core/encoding/_load_spec.rb5
-rw-r--r--spec/rubyspec/core/encoding/aliases_spec.rb45
-rw-r--r--spec/rubyspec/core/encoding/ascii_compatible_spec.rb13
-rw-r--r--spec/rubyspec/core/encoding/compatible_spec.rb381
-rw-r--r--spec/rubyspec/core/encoding/converter/asciicompat_encoding_spec.rb39
-rw-r--r--spec/rubyspec/core/encoding/converter/constants_spec.rb133
-rw-r--r--spec/rubyspec/core/encoding/converter/convert_spec.rb47
-rw-r--r--spec/rubyspec/core/encoding/converter/convpath_spec.rb65
-rw-r--r--spec/rubyspec/core/encoding/converter/destination_encoding_spec.rb13
-rw-r--r--spec/rubyspec/core/encoding/converter/finish_spec.rb38
-rw-r--r--spec/rubyspec/core/encoding/converter/insert_output_spec.rb5
-rw-r--r--spec/rubyspec/core/encoding/converter/inspect_spec.rb13
-rw-r--r--spec/rubyspec/core/encoding/converter/last_error_spec.rb85
-rw-r--r--spec/rubyspec/core/encoding/converter/new_spec.rb121
-rw-r--r--spec/rubyspec/core/encoding/converter/primitive_convert_spec.rb213
-rw-r--r--spec/rubyspec/core/encoding/converter/primitive_errinfo_spec.rb72
-rw-r--r--spec/rubyspec/core/encoding/converter/putback_spec.rb50
-rw-r--r--spec/rubyspec/core/encoding/converter/replacement_spec.rb74
-rw-r--r--spec/rubyspec/core/encoding/converter/search_convpath_spec.rb73
-rw-r--r--spec/rubyspec/core/encoding/converter/source_encoding_spec.rb13
-rw-r--r--spec/rubyspec/core/encoding/default_external_spec.rb74
-rw-r--r--spec/rubyspec/core/encoding/default_internal_spec.rb93
-rw-r--r--spec/rubyspec/core/encoding/dummy_spec.rb16
-rw-r--r--spec/rubyspec/core/encoding/find_spec.rb84
-rw-r--r--spec/rubyspec/core/encoding/fixtures/classes.rb49
-rw-r--r--spec/rubyspec/core/encoding/inspect_spec.rb21
-rw-r--r--spec/rubyspec/core/encoding/invalid_byte_sequence_error/destination_encoding_name_spec.rb20
-rw-r--r--spec/rubyspec/core/encoding/invalid_byte_sequence_error/destination_encoding_spec.rb20
-rw-r--r--spec/rubyspec/core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb32
-rw-r--r--spec/rubyspec/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb31
-rw-r--r--spec/rubyspec/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb32
-rw-r--r--spec/rubyspec/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb30
-rw-r--r--spec/rubyspec/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb35
-rw-r--r--spec/rubyspec/core/encoding/list_spec.rb43
-rw-r--r--spec/rubyspec/core/encoding/locale_charmap_spec.rb47
-rw-r--r--spec/rubyspec/core/encoding/name_list_spec.rb25
-rw-r--r--spec/rubyspec/core/encoding/name_spec.rb7
-rw-r--r--spec/rubyspec/core/encoding/names_spec.rb37
-rw-r--r--spec/rubyspec/core/encoding/replicate_spec.rb48
-rw-r--r--spec/rubyspec/core/encoding/shared/name.rb15
-rw-r--r--spec/rubyspec/core/encoding/to_s_spec.rb7
-rw-r--r--spec/rubyspec/core/encoding/undefined_conversion_error/destination_encoding_name_spec.rb17
-rw-r--r--spec/rubyspec/core/encoding/undefined_conversion_error/destination_encoding_spec.rb17
-rw-r--r--spec/rubyspec/core/encoding/undefined_conversion_error/error_char_spec.rb29
-rw-r--r--spec/rubyspec/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb30
-rw-r--r--spec/rubyspec/core/encoding/undefined_conversion_error/source_encoding_spec.rb31
-rw-r--r--spec/rubyspec/core/enumerable/all_spec.rb121
-rw-r--r--spec/rubyspec/core/enumerable/any_spec.rb141
-rw-r--r--spec/rubyspec/core/enumerable/chunk_spec.rb100
-rw-r--r--spec/rubyspec/core/enumerable/chunk_while_spec.rb44
-rw-r--r--spec/rubyspec/core/enumerable/collect_concat_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerable/collect_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerable/count_spec.rb59
-rw-r--r--spec/rubyspec/core/enumerable/cycle_spec.rb104
-rw-r--r--spec/rubyspec/core/enumerable/detect_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerable/drop_spec.rb43
-rw-r--r--spec/rubyspec/core/enumerable/drop_while_spec.rb50
-rw-r--r--spec/rubyspec/core/enumerable/each_cons_spec.rb99
-rw-r--r--spec/rubyspec/core/enumerable/each_entry_spec.rb41
-rw-r--r--spec/rubyspec/core/enumerable/each_slice_spec.rb101
-rw-r--r--spec/rubyspec/core/enumerable/each_with_index_spec.rb53
-rw-r--r--spec/rubyspec/core/enumerable/each_with_object_spec.rb41
-rw-r--r--spec/rubyspec/core/enumerable/entries_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerable/find_all_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerable/find_index_spec.rb89
-rw-r--r--spec/rubyspec/core/enumerable/find_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerable/first_spec.rb28
-rw-r--r--spec/rubyspec/core/enumerable/fixtures/classes.rb331
-rw-r--r--spec/rubyspec/core/enumerable/flat_map_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerable/grep_spec.rb52
-rw-r--r--spec/rubyspec/core/enumerable/grep_v_spec.rb43
-rw-r--r--spec/rubyspec/core/enumerable/group_by_spec.rb45
-rw-r--r--spec/rubyspec/core/enumerable/include_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerable/inject_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerable/lazy_spec.rb10
-rw-r--r--spec/rubyspec/core/enumerable/map_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerable/max_by_spec.rb81
-rw-r--r--spec/rubyspec/core/enumerable/max_spec.rb119
-rw-r--r--spec/rubyspec/core/enumerable/member_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerable/min_by_spec.rb81
-rw-r--r--spec/rubyspec/core/enumerable/min_spec.rb123
-rw-r--r--spec/rubyspec/core/enumerable/minmax_by_spec.rb44
-rw-r--r--spec/rubyspec/core/enumerable/minmax_spec.rb44
-rw-r--r--spec/rubyspec/core/enumerable/none_spec.rb57
-rw-r--r--spec/rubyspec/core/enumerable/one_spec.rb49
-rw-r--r--spec/rubyspec/core/enumerable/partition_spec.rb20
-rw-r--r--spec/rubyspec/core/enumerable/reduce_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerable/reject_spec.rb25
-rw-r--r--spec/rubyspec/core/enumerable/reverse_each_spec.rb26
-rw-r--r--spec/rubyspec/core/enumerable/select_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerable/shared/collect.rb32
-rw-r--r--spec/rubyspec/core/enumerable/shared/collect_concat.rb54
-rw-r--r--spec/rubyspec/core/enumerable/shared/entries.rb24
-rw-r--r--spec/rubyspec/core/enumerable/shared/enumerable_enumeratorized.rb33
-rw-r--r--spec/rubyspec/core/enumerable/shared/enumeratorized.rb42
-rw-r--r--spec/rubyspec/core/enumerable/shared/find.rb73
-rw-r--r--spec/rubyspec/core/enumerable/shared/find_all.rb31
-rw-r--r--spec/rubyspec/core/enumerable/shared/include.rb34
-rw-r--r--spec/rubyspec/core/enumerable/shared/inject.rb69
-rw-r--r--spec/rubyspec/core/enumerable/shared/take.rb63
-rw-r--r--spec/rubyspec/core/enumerable/slice_after_spec.rb61
-rw-r--r--spec/rubyspec/core/enumerable/slice_before_spec.rb87
-rw-r--r--spec/rubyspec/core/enumerable/slice_when_spec.rb54
-rw-r--r--spec/rubyspec/core/enumerable/sort_by_spec.rb36
-rw-r--r--spec/rubyspec/core/enumerable/sort_spec.rb54
-rw-r--r--spec/rubyspec/core/enumerable/take_spec.rb13
-rw-r--r--spec/rubyspec/core/enumerable/take_while_spec.rb51
-rw-r--r--spec/rubyspec/core/enumerable/to_a_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerable/to_h_spec.rb46
-rw-r--r--spec/rubyspec/core/enumerable/zip_spec.rb42
-rw-r--r--spec/rubyspec/core/enumerator/each_spec.rb5
-rw-r--r--spec/rubyspec/core/enumerator/each_with_index_spec.rb38
-rw-r--r--spec/rubyspec/core/enumerator/each_with_object_spec.rb6
-rw-r--r--spec/rubyspec/core/enumerator/enum_for_spec.rb6
-rw-r--r--spec/rubyspec/core/enumerator/enumerator_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerator/feed_spec.rb52
-rw-r--r--spec/rubyspec/core/enumerator/first_spec.rb7
-rw-r--r--spec/rubyspec/core/enumerator/fixtures/common.rb9
-rw-r--r--spec/rubyspec/core/enumerator/generator/each_spec.rb40
-rw-r--r--spec/rubyspec/core/enumerator/generator/initialize_spec.rb26
-rw-r--r--spec/rubyspec/core/enumerator/initialize_spec.rb61
-rw-r--r--spec/rubyspec/core/enumerator/inject_spec.rb15
-rw-r--r--spec/rubyspec/core/enumerator/inspect_spec.rb5
-rw-r--r--spec/rubyspec/core/enumerator/lazy/collect_concat_spec.rb8
-rw-r--r--spec/rubyspec/core/enumerator/lazy/collect_spec.rb8
-rw-r--r--spec/rubyspec/core/enumerator/lazy/drop_spec.rb52
-rw-r--r--spec/rubyspec/core/enumerator/lazy/drop_while_spec.rb60
-rw-r--r--spec/rubyspec/core/enumerator/lazy/enum_for_spec.rb8
-rw-r--r--spec/rubyspec/core/enumerator/lazy/find_all_spec.rb8
-rw-r--r--spec/rubyspec/core/enumerator/lazy/fixtures/classes.rb54
-rw-r--r--spec/rubyspec/core/enumerator/lazy/flat_map_spec.rb8
-rw-r--r--spec/rubyspec/core/enumerator/lazy/force_spec.rb30
-rw-r--r--spec/rubyspec/core/enumerator/lazy/grep_spec.rb82
-rw-r--r--spec/rubyspec/core/enumerator/lazy/grep_v_spec.rb86
-rw-r--r--spec/rubyspec/core/enumerator/lazy/initialize_spec.rb63
-rw-r--r--spec/rubyspec/core/enumerator/lazy/lazy_spec.rb16
-rw-r--r--spec/rubyspec/core/enumerator/lazy/map_spec.rb12
-rw-r--r--spec/rubyspec/core/enumerator/lazy/reject_spec.rb60
-rw-r--r--spec/rubyspec/core/enumerator/lazy/select_spec.rb8
-rw-r--r--spec/rubyspec/core/enumerator/lazy/shared/collect.rb56
-rw-r--r--spec/rubyspec/core/enumerator/lazy/shared/collect_concat.rb72
-rw-r--r--spec/rubyspec/core/enumerator/lazy/shared/select.rb60
-rw-r--r--spec/rubyspec/core/enumerator/lazy/shared/to_enum.rb50
-rw-r--r--spec/rubyspec/core/enumerator/lazy/take_spec.rb66
-rw-r--r--spec/rubyspec/core/enumerator/lazy/take_while_spec.rb60
-rw-r--r--spec/rubyspec/core/enumerator/lazy/to_enum_spec.rb8
-rw-r--r--spec/rubyspec/core/enumerator/lazy/zip_spec.rb74
-rw-r--r--spec/rubyspec/core/enumerator/new_spec.rb6
-rw-r--r--spec/rubyspec/core/enumerator/next_spec.rb6
-rw-r--r--spec/rubyspec/core/enumerator/next_values_spec.rb55
-rw-r--r--spec/rubyspec/core/enumerator/peek_spec.rb36
-rw-r--r--spec/rubyspec/core/enumerator/peek_values_spec.rb57
-rw-r--r--spec/rubyspec/core/enumerator/rewind_spec.rb38
-rw-r--r--spec/rubyspec/core/enumerator/size_spec.rb26
-rw-r--r--spec/rubyspec/core/enumerator/to_enum_spec.rb6
-rw-r--r--spec/rubyspec/core/enumerator/with_index_spec.rb72
-rw-r--r--spec/rubyspec/core/enumerator/with_object_spec.rb6
-rw-r--r--spec/rubyspec/core/enumerator/yielder/append_spec.rb35
-rw-r--r--spec/rubyspec/core/enumerator/yielder/initialize_spec.rb18
-rw-r--r--spec/rubyspec/core/enumerator/yielder/yield_spec.rb16
-rw-r--r--spec/rubyspec/core/env/assoc_spec.rb23
-rw-r--r--spec/rubyspec/core/env/clear_spec.rb20
-rw-r--r--spec/rubyspec/core/env/delete_if_spec.rb27
-rw-r--r--spec/rubyspec/core/env/delete_spec.rb24
-rw-r--r--spec/rubyspec/core/env/each_key_spec.rb32
-rw-r--r--spec/rubyspec/core/env/each_pair_spec.rb6
-rw-r--r--spec/rubyspec/core/env/each_spec.rb6
-rw-r--r--spec/rubyspec/core/env/each_value_spec.rb32
-rw-r--r--spec/rubyspec/core/env/element_reference_spec.rb66
-rw-r--r--spec/rubyspec/core/env/element_set_spec.rb6
-rw-r--r--spec/rubyspec/core/env/empty_spec.rb23
-rw-r--r--spec/rubyspec/core/env/fetch_spec.rb35
-rw-r--r--spec/rubyspec/core/env/has_key_spec.rb6
-rw-r--r--spec/rubyspec/core/env/has_value_spec.rb6
-rw-r--r--spec/rubyspec/core/env/include_spec.rb6
-rw-r--r--spec/rubyspec/core/env/index_spec.rb6
-rw-r--r--spec/rubyspec/core/env/indexes_spec.rb1
-rw-r--r--spec/rubyspec/core/env/indices_spec.rb1
-rw-r--r--spec/rubyspec/core/env/inspect_spec.rb11
-rw-r--r--spec/rubyspec/core/env/invert_spec.rb16
-rw-r--r--spec/rubyspec/core/env/keep_if_spec.rb33
-rw-r--r--spec/rubyspec/core/env/key_spec.rb11
-rw-r--r--spec/rubyspec/core/env/keys_spec.rb14
-rw-r--r--spec/rubyspec/core/env/length_spec.rb6
-rw-r--r--spec/rubyspec/core/env/member_spec.rb6
-rw-r--r--spec/rubyspec/core/env/rassoc_spec.rb23
-rw-r--r--spec/rubyspec/core/env/rehash_spec.rb1
-rw-r--r--spec/rubyspec/core/env/reject_spec.rb77
-rw-r--r--spec/rubyspec/core/env/replace_spec.rb15
-rw-r--r--spec/rubyspec/core/env/select_spec.rb39
-rw-r--r--spec/rubyspec/core/env/shared/each.rb65
-rw-r--r--spec/rubyspec/core/env/shared/include.rb11
-rw-r--r--spec/rubyspec/core/env/shared/key.rb15
-rw-r--r--spec/rubyspec/core/env/shared/length.rb13
-rw-r--r--spec/rubyspec/core/env/shared/store.rb56
-rw-r--r--spec/rubyspec/core/env/shared/to_hash.rb22
-rw-r--r--spec/rubyspec/core/env/shared/value.rb11
-rw-r--r--spec/rubyspec/core/env/shift_spec.rb59
-rw-r--r--spec/rubyspec/core/env/size_spec.rb6
-rw-r--r--spec/rubyspec/core/env/store_spec.rb6
-rw-r--r--spec/rubyspec/core/env/to_a_spec.rb19
-rw-r--r--spec/rubyspec/core/env/to_h_spec.rb6
-rw-r--r--spec/rubyspec/core/env/to_hash_spec.rb6
-rw-r--r--spec/rubyspec/core/env/to_s_spec.rb7
-rw-r--r--spec/rubyspec/core/env/update_spec.rb25
-rw-r--r--spec/rubyspec/core/env/value_spec.rb6
-rw-r--r--spec/rubyspec/core/env/values_at_spec.rb17
-rw-r--r--spec/rubyspec/core/env/values_spec.rb21
-rw-r--r--spec/rubyspec/core/exception/args_spec.rb5
-rw-r--r--spec/rubyspec/core/exception/arguments_spec.rb11
-rw-r--r--spec/rubyspec/core/exception/backtrace_spec.rb68
-rw-r--r--spec/rubyspec/core/exception/case_compare_spec.rb5
-rw-r--r--spec/rubyspec/core/exception/cause_spec.rb19
-rw-r--r--spec/rubyspec/core/exception/destination_encoding_name_spec.rb9
-rw-r--r--spec/rubyspec/core/exception/destination_encoding_spec.rb9
-rw-r--r--spec/rubyspec/core/exception/equal_value_spec.rb68
-rw-r--r--spec/rubyspec/core/exception/errno_spec.rb48
-rw-r--r--spec/rubyspec/core/exception/error_bytes_spec.rb5
-rw-r--r--spec/rubyspec/core/exception/error_char_spec.rb5
-rw-r--r--spec/rubyspec/core/exception/exception_spec.rb83
-rw-r--r--spec/rubyspec/core/exception/exit_value_spec.rb5
-rw-r--r--spec/rubyspec/core/exception/fixtures/common.rb64
-rw-r--r--spec/rubyspec/core/exception/incomplete_input_spec.rb5
-rw-r--r--spec/rubyspec/core/exception/initialize_spec.rb1
-rw-r--r--spec/rubyspec/core/exception/inspect_spec.rb20
-rw-r--r--spec/rubyspec/core/exception/interrupt_spec.rb41
-rw-r--r--spec/rubyspec/core/exception/io_error_spec.rb51
-rw-r--r--spec/rubyspec/core/exception/load_error_spec.rb21
-rw-r--r--spec/rubyspec/core/exception/message_spec.rb27
-rw-r--r--spec/rubyspec/core/exception/name_error_spec.rb13
-rw-r--r--spec/rubyspec/core/exception/name_spec.rb61
-rw-r--r--spec/rubyspec/core/exception/new_spec.rb7
-rw-r--r--spec/rubyspec/core/exception/no_method_error_spec.rb59
-rw-r--r--spec/rubyspec/core/exception/range_error_spec.rb7
-rw-r--r--spec/rubyspec/core/exception/readagain_bytes_spec.rb5
-rw-r--r--spec/rubyspec/core/exception/reason_spec.rb5
-rw-r--r--spec/rubyspec/core/exception/receiver_spec.rb62
-rw-r--r--spec/rubyspec/core/exception/result_spec.rb29
-rw-r--r--spec/rubyspec/core/exception/script_error_spec.rb15
-rw-r--r--spec/rubyspec/core/exception/set_backtrace_spec.rb56
-rw-r--r--spec/rubyspec/core/exception/shared/new.rb18
-rw-r--r--spec/rubyspec/core/exception/signal_exception_spec.rb74
-rw-r--r--spec/rubyspec/core/exception/signm_spec.rb5
-rw-r--r--spec/rubyspec/core/exception/signo_spec.rb5
-rw-r--r--spec/rubyspec/core/exception/source_encoding_name_spec.rb9
-rw-r--r--spec/rubyspec/core/exception/source_encoding_spec.rb9
-rw-r--r--spec/rubyspec/core/exception/standard_error_spec.rb50
-rw-r--r--spec/rubyspec/core/exception/status_spec.rb5
-rw-r--r--spec/rubyspec/core/exception/success_spec.rb5
-rw-r--r--spec/rubyspec/core/exception/system_call_error_spec.rb89
-rw-r--r--spec/rubyspec/core/exception/system_stack_error_spec.rb7
-rw-r--r--spec/rubyspec/core/exception/to_s_spec.rb23
-rw-r--r--spec/rubyspec/core/false/and_spec.rb11
-rw-r--r--spec/rubyspec/core/false/inspect_spec.rb7
-rw-r--r--spec/rubyspec/core/false/or_spec.rb11
-rw-r--r--spec/rubyspec/core/false/to_s_spec.rb7
-rw-r--r--spec/rubyspec/core/false/xor_spec.rb11
-rw-r--r--spec/rubyspec/core/fiber/new_spec.rb41
-rw-r--r--spec/rubyspec/core/fiber/resume_spec.rb54
-rw-r--r--spec/rubyspec/core/fiber/yield_spec.rb36
-rw-r--r--spec/rubyspec/core/file/absolute_path_spec.rb37
-rw-r--r--spec/rubyspec/core/file/atime_spec.rb55
-rw-r--r--spec/rubyspec/core/file/basename_spec.rb170
-rw-r--r--spec/rubyspec/core/file/birthtime_spec.rb56
-rw-r--r--spec/rubyspec/core/file/blockdev_spec.rb6
-rw-r--r--spec/rubyspec/core/file/chardev_spec.rb6
-rw-r--r--spec/rubyspec/core/file/chmod_spec.rb239
-rw-r--r--spec/rubyspec/core/file/chown_spec.rb152
-rw-r--r--spec/rubyspec/core/file/constants/constants_spec.rb31
-rw-r--r--spec/rubyspec/core/file/constants_spec.rb141
-rw-r--r--spec/rubyspec/core/file/ctime_spec.rb51
-rw-r--r--spec/rubyspec/core/file/delete_spec.rb6
-rw-r--r--spec/rubyspec/core/file/directory_spec.rb10
-rw-r--r--spec/rubyspec/core/file/dirname_spec.rb108
-rw-r--r--spec/rubyspec/core/file/executable_real_spec.rb7
-rw-r--r--spec/rubyspec/core/file/executable_spec.rb7
-rw-r--r--spec/rubyspec/core/file/exist_spec.rb6
-rw-r--r--spec/rubyspec/core/file/exists_spec.rb6
-rw-r--r--spec/rubyspec/core/file/expand_path_spec.rb242
-rw-r--r--spec/rubyspec/core/file/extname_spec.rb54
-rw-r--r--spec/rubyspec/core/file/file_spec.rb16
-rw-r--r--spec/rubyspec/core/file/fixtures/common.rb22
-rw-r--r--spec/rubyspec/core/file/fixtures/do_not_remove0
-rw-r--r--spec/rubyspec/core/file/fixtures/file_types.rb65
-rw-r--r--spec/rubyspec/core/file/flock_spec.rb106
-rw-r--r--spec/rubyspec/core/file/fnmatch_spec.rb10
-rw-r--r--spec/rubyspec/core/file/ftype_spec.rb69
-rw-r--r--spec/rubyspec/core/file/grpowned_spec.rb10
-rw-r--r--spec/rubyspec/core/file/identical_spec.rb6
-rw-r--r--spec/rubyspec/core/file/initialize_spec.rb23
-rw-r--r--spec/rubyspec/core/file/inspect_spec.rb17
-rw-r--r--spec/rubyspec/core/file/join_spec.rb139
-rw-r--r--spec/rubyspec/core/file/lchmod_spec.rb42
-rw-r--r--spec/rubyspec/core/file/lchown_spec.rb63
-rw-r--r--spec/rubyspec/core/file/link_spec.rb39
-rw-r--r--spec/rubyspec/core/file/lstat_spec.rb33
-rw-r--r--spec/rubyspec/core/file/mkfifo_spec.rb53
-rw-r--r--spec/rubyspec/core/file/mtime_spec.rb51
-rw-r--r--spec/rubyspec/core/file/new_spec.rb162
-rw-r--r--spec/rubyspec/core/file/null_spec.rb15
-rw-r--r--spec/rubyspec/core/file/open_spec.rb676
-rw-r--r--spec/rubyspec/core/file/owned_spec.rb33
-rw-r--r--spec/rubyspec/core/file/path_spec.rb29
-rw-r--r--spec/rubyspec/core/file/pipe_spec.rb32
-rw-r--r--spec/rubyspec/core/file/read_spec.rb6
-rw-r--r--spec/rubyspec/core/file/readable_real_spec.rb7
-rw-r--r--spec/rubyspec/core/file/readable_spec.rb7
-rw-r--r--spec/rubyspec/core/file/readlink_spec.rb67
-rw-r--r--spec/rubyspec/core/file/realdirpath_spec.rb100
-rw-r--r--spec/rubyspec/core/file/realpath_spec.rb80
-rw-r--r--spec/rubyspec/core/file/rename_spec.rb37
-rw-r--r--spec/rubyspec/core/file/reopen_spec.rb32
-rw-r--r--spec/rubyspec/core/file/setgid_spec.rb36
-rw-r--r--spec/rubyspec/core/file/setuid_spec.rb38
-rw-r--r--spec/rubyspec/core/file/shared/fnmatch.rb241
-rw-r--r--spec/rubyspec/core/file/shared/open.rb12
-rw-r--r--spec/rubyspec/core/file/shared/read.rb15
-rw-r--r--spec/rubyspec/core/file/shared/stat.rb32
-rw-r--r--spec/rubyspec/core/file/shared/unlink.rb63
-rw-r--r--spec/rubyspec/core/file/size_spec.rb119
-rw-r--r--spec/rubyspec/core/file/socket_spec.rb42
-rw-r--r--spec/rubyspec/core/file/split_spec.rb63
-rw-r--r--spec/rubyspec/core/file/stat/atime_spec.rb18
-rw-r--r--spec/rubyspec/core/file/stat/birthtime_spec.rb27
-rw-r--r--spec/rubyspec/core/file/stat/blksize_spec.rb27
-rw-r--r--spec/rubyspec/core/file/stat/blockdev_spec.rb7
-rw-r--r--spec/rubyspec/core/file/stat/blocks_spec.rb27
-rw-r--r--spec/rubyspec/core/file/stat/chardev_spec.rb7
-rw-r--r--spec/rubyspec/core/file/stat/comparison_spec.rb66
-rw-r--r--spec/rubyspec/core/file/stat/ctime_spec.rb18
-rw-r--r--spec/rubyspec/core/file/stat/dev_major_spec.rb23
-rw-r--r--spec/rubyspec/core/file/stat/dev_minor_spec.rb23
-rw-r--r--spec/rubyspec/core/file/stat/dev_spec.rb15
-rw-r--r--spec/rubyspec/core/file/stat/directory_spec.rb7
-rw-r--r--spec/rubyspec/core/file/stat/executable_real_spec.rb7
-rw-r--r--spec/rubyspec/core/file/stat/executable_spec.rb7
-rw-r--r--spec/rubyspec/core/file/stat/file_spec.rb7
-rw-r--r--spec/rubyspec/core/file/stat/fixtures/classes.rb5
-rw-r--r--spec/rubyspec/core/file/stat/ftype_spec.rb65
-rw-r--r--spec/rubyspec/core/file/stat/gid_spec.rb19
-rw-r--r--spec/rubyspec/core/file/stat/grpowned_spec.rb7
-rw-r--r--spec/rubyspec/core/file/stat/ino_spec.rb38
-rw-r--r--spec/rubyspec/core/file/stat/inspect_spec.rb26
-rw-r--r--spec/rubyspec/core/file/stat/mode_spec.rb19
-rw-r--r--spec/rubyspec/core/file/stat/mtime_spec.rb18
-rw-r--r--spec/rubyspec/core/file/stat/new_spec.rb30
-rw-r--r--spec/rubyspec/core/file/stat/nlink_spec.rb21
-rw-r--r--spec/rubyspec/core/file/stat/owned_spec.rb31
-rw-r--r--spec/rubyspec/core/file/stat/pipe_spec.rb32
-rw-r--r--spec/rubyspec/core/file/stat/rdev_major_spec.rb31
-rw-r--r--spec/rubyspec/core/file/stat/rdev_minor_spec.rb31
-rw-r--r--spec/rubyspec/core/file/stat/rdev_spec.rb15
-rw-r--r--spec/rubyspec/core/file/stat/readable_real_spec.rb7
-rw-r--r--spec/rubyspec/core/file/stat/readable_spec.rb7
-rw-r--r--spec/rubyspec/core/file/stat/setgid_spec.rb11
-rw-r--r--spec/rubyspec/core/file/stat/setuid_spec.rb11
-rw-r--r--spec/rubyspec/core/file/stat/size_spec.rb21
-rw-r--r--spec/rubyspec/core/file/stat/socket_spec.rb11
-rw-r--r--spec/rubyspec/core/file/stat/sticky_spec.rb11
-rw-r--r--spec/rubyspec/core/file/stat/symlink_spec.rb7
-rw-r--r--spec/rubyspec/core/file/stat/uid_spec.rb18
-rw-r--r--spec/rubyspec/core/file/stat/world_readable_spec.rb11
-rw-r--r--spec/rubyspec/core/file/stat/world_writable_spec.rb11
-rw-r--r--spec/rubyspec/core/file/stat/writable_real_spec.rb7
-rw-r--r--spec/rubyspec/core/file/stat/writable_spec.rb7
-rw-r--r--spec/rubyspec/core/file/stat/zero_spec.rb7
-rw-r--r--spec/rubyspec/core/file/stat_spec.rb45
-rw-r--r--spec/rubyspec/core/file/sticky_spec.rb50
-rw-r--r--spec/rubyspec/core/file/symlink_spec.rb57
-rw-r--r--spec/rubyspec/core/file/to_path_spec.rb49
-rw-r--r--spec/rubyspec/core/file/truncate_spec.rb177
-rw-r--r--spec/rubyspec/core/file/umask_spec.rb60
-rw-r--r--spec/rubyspec/core/file/unlink_spec.rb6
-rw-r--r--spec/rubyspec/core/file/utime_spec.rb36
-rw-r--r--spec/rubyspec/core/file/world_readable_spec.rb12
-rw-r--r--spec/rubyspec/core/file/world_writable_spec.rb12
-rw-r--r--spec/rubyspec/core/file/writable_real_spec.rb7
-rw-r--r--spec/rubyspec/core/file/writable_spec.rb7
-rw-r--r--spec/rubyspec/core/file/zero_spec.rb13
-rw-r--r--spec/rubyspec/core/filetest/blockdev_spec.rb6
-rw-r--r--spec/rubyspec/core/filetest/chardev_spec.rb6
-rw-r--r--spec/rubyspec/core/filetest/directory_spec.rb10
-rw-r--r--spec/rubyspec/core/filetest/executable_real_spec.rb7
-rw-r--r--spec/rubyspec/core/filetest/executable_spec.rb7
-rw-r--r--spec/rubyspec/core/filetest/exist_spec.rb6
-rw-r--r--spec/rubyspec/core/filetest/exists_spec.rb6
-rw-r--r--spec/rubyspec/core/filetest/file_spec.rb10
-rw-r--r--spec/rubyspec/core/filetest/grpowned_spec.rb10
-rw-r--r--spec/rubyspec/core/filetest/identical_spec.rb6
-rw-r--r--spec/rubyspec/core/filetest/owned_spec.rb10
-rw-r--r--spec/rubyspec/core/filetest/pipe_spec.rb10
-rw-r--r--spec/rubyspec/core/filetest/readable_real_spec.rb7
-rw-r--r--spec/rubyspec/core/filetest/readable_spec.rb7
-rw-r--r--spec/rubyspec/core/filetest/setgid_spec.rb10
-rw-r--r--spec/rubyspec/core/filetest/setuid_spec.rb10
-rw-r--r--spec/rubyspec/core/filetest/size_spec.rb34
-rw-r--r--spec/rubyspec/core/filetest/socket_spec.rb10
-rw-r--r--spec/rubyspec/core/filetest/sticky_spec.rb7
-rw-r--r--spec/rubyspec/core/filetest/symlink_spec.rb10
-rw-r--r--spec/rubyspec/core/filetest/world_readable_spec.rb5
-rw-r--r--spec/rubyspec/core/filetest/world_writable_spec.rb5
-rw-r--r--spec/rubyspec/core/filetest/writable_real_spec.rb7
-rw-r--r--spec/rubyspec/core/filetest/writable_spec.rb7
-rw-r--r--spec/rubyspec/core/filetest/zero_spec.rb13
-rw-r--r--spec/rubyspec/core/fixnum/abs_spec.rb7
-rw-r--r--spec/rubyspec/core/fixnum/bit_and_spec.rb25
-rw-r--r--spec/rubyspec/core/fixnum/bit_length_spec.rb42
-rw-r--r--spec/rubyspec/core/fixnum/bit_or_spec.rb26
-rw-r--r--spec/rubyspec/core/fixnum/bit_xor_spec.rb24
-rw-r--r--spec/rubyspec/core/fixnum/case_compare_spec.rb6
-rw-r--r--spec/rubyspec/core/fixnum/coerce_spec.rb39
-rw-r--r--spec/rubyspec/core/fixnum/comparison_spec.rb26
-rw-r--r--spec/rubyspec/core/fixnum/complement_spec.rb10
-rw-r--r--spec/rubyspec/core/fixnum/div_spec.rb44
-rw-r--r--spec/rubyspec/core/fixnum/divide_spec.rb35
-rw-r--r--spec/rubyspec/core/fixnum/divmod_spec.rb35
-rw-r--r--spec/rubyspec/core/fixnum/element_reference_spec.rb80
-rw-r--r--spec/rubyspec/core/fixnum/equal_value_spec.rb6
-rw-r--r--spec/rubyspec/core/fixnum/even_spec.rb23
-rw-r--r--spec/rubyspec/core/fixnum/exponent_spec.rb76
-rw-r--r--spec/rubyspec/core/fixnum/fdiv_spec.rb49
-rw-r--r--spec/rubyspec/core/fixnum/fixnum_spec.rb7
-rw-r--r--spec/rubyspec/core/fixnum/gt_spec.rb19
-rw-r--r--spec/rubyspec/core/fixnum/gte_spec.rb20
-rw-r--r--spec/rubyspec/core/fixnum/hash_spec.rb11
-rw-r--r--spec/rubyspec/core/fixnum/left_shift_spec.rb91
-rw-r--r--spec/rubyspec/core/fixnum/lt_spec.rb19
-rw-r--r--spec/rubyspec/core/fixnum/lte_spec.rb20
-rw-r--r--spec/rubyspec/core/fixnum/magnitude_spec.rb6
-rw-r--r--spec/rubyspec/core/fixnum/minus_spec.rb29
-rw-r--r--spec/rubyspec/core/fixnum/modulo_spec.rb10
-rw-r--r--spec/rubyspec/core/fixnum/multiply_spec.rb27
-rw-r--r--spec/rubyspec/core/fixnum/odd_spec.rb23
-rw-r--r--spec/rubyspec/core/fixnum/plus_spec.rb29
-rw-r--r--spec/rubyspec/core/fixnum/right_shift_spec.rb91
-rw-r--r--spec/rubyspec/core/fixnum/shared/abs.rb9
-rw-r--r--spec/rubyspec/core/fixnum/shared/equal.rb24
-rw-r--r--spec/rubyspec/core/fixnum/shared/modulo.rb42
-rw-r--r--spec/rubyspec/core/fixnum/size_spec.rb19
-rw-r--r--spec/rubyspec/core/fixnum/succ_spec.rb15
-rw-r--r--spec/rubyspec/core/fixnum/to_f_spec.rb9
-rw-r--r--spec/rubyspec/core/fixnum/to_s_spec.rb50
-rw-r--r--spec/rubyspec/core/fixnum/uminus_spec.rb16
-rw-r--r--spec/rubyspec/core/fixnum/zero_spec.rb9
-rw-r--r--spec/rubyspec/core/float/abs_spec.rb5
-rw-r--r--spec/rubyspec/core/float/angle_spec.rb5
-rw-r--r--spec/rubyspec/core/float/arg_spec.rb5
-rw-r--r--spec/rubyspec/core/float/case_compare_spec.rb6
-rw-r--r--spec/rubyspec/core/float/ceil_spec.rb13
-rw-r--r--spec/rubyspec/core/float/coerce_spec.rb18
-rw-r--r--spec/rubyspec/core/float/comparison_spec.rb36
-rw-r--r--spec/rubyspec/core/float/constants_spec.rb55
-rw-r--r--spec/rubyspec/core/float/denominator_spec.rb29
-rw-r--r--spec/rubyspec/core/float/divide_spec.rb36
-rw-r--r--spec/rubyspec/core/float/divmod_spec.rb43
-rw-r--r--spec/rubyspec/core/float/eql_spec.rb16
-rw-r--r--spec/rubyspec/core/float/equal_value_spec.rb6
-rw-r--r--spec/rubyspec/core/float/exponent_spec.rb15
-rw-r--r--spec/rubyspec/core/float/fdiv_spec.rb6
-rw-r--r--spec/rubyspec/core/float/finite_spec.rb19
-rw-r--r--spec/rubyspec/core/float/fixtures/coerce.rb15
-rw-r--r--spec/rubyspec/core/float/float_spec.rb7
-rw-r--r--spec/rubyspec/core/float/floor_spec.rb13
-rw-r--r--spec/rubyspec/core/float/gt_spec.rb14
-rw-r--r--spec/rubyspec/core/float/gte_spec.rb14
-rw-r--r--spec/rubyspec/core/float/hash_spec.rb11
-rw-r--r--spec/rubyspec/core/float/infinite_spec.rb19
-rw-r--r--spec/rubyspec/core/float/lt_spec.rb14
-rw-r--r--spec/rubyspec/core/float/lte_spec.rb15
-rw-r--r--spec/rubyspec/core/float/magnitude_spec.rb5
-rw-r--r--spec/rubyspec/core/float/minus_spec.rb9
-rw-r--r--spec/rubyspec/core/float/modulo_spec.rb10
-rw-r--r--spec/rubyspec/core/float/multiply_spec.rb14
-rw-r--r--spec/rubyspec/core/float/nan_spec.rb9
-rw-r--r--spec/rubyspec/core/float/next_float_spec.rb49
-rw-r--r--spec/rubyspec/core/float/numerator_spec.rb39
-rw-r--r--spec/rubyspec/core/float/phase_spec.rb5
-rw-r--r--spec/rubyspec/core/float/plus_spec.rb9
-rw-r--r--spec/rubyspec/core/float/prev_float_spec.rb49
-rw-r--r--spec/rubyspec/core/float/quo_spec.rb6
-rw-r--r--spec/rubyspec/core/float/rationalize_spec.rb43
-rw-r--r--spec/rubyspec/core/float/round_spec.rb87
-rw-r--r--spec/rubyspec/core/float/shared/abs.rb21
-rw-r--r--spec/rubyspec/core/float/shared/equal.rb17
-rw-r--r--spec/rubyspec/core/float/shared/modulo.rb42
-rw-r--r--spec/rubyspec/core/float/shared/quo.rb59
-rw-r--r--spec/rubyspec/core/float/shared/to_i.rb10
-rw-r--r--spec/rubyspec/core/float/to_f_spec.rb9
-rw-r--r--spec/rubyspec/core/float/to_i_spec.rb6
-rw-r--r--spec/rubyspec/core/float/to_int_spec.rb6
-rw-r--r--spec/rubyspec/core/float/to_r_spec.rb5
-rw-r--r--spec/rubyspec/core/float/to_s_spec.rb120
-rw-r--r--spec/rubyspec/core/float/truncate_spec.rb6
-rw-r--r--spec/rubyspec/core/float/uminus_spec.rb28
-rw-r--r--spec/rubyspec/core/float/uplus_spec.rb9
-rw-r--r--spec/rubyspec/core/float/zero_spec.rb9
-rw-r--r--spec/rubyspec/core/gc/count_spec.rb7
-rw-r--r--spec/rubyspec/core/gc/disable_spec.rb18
-rw-r--r--spec/rubyspec/core/gc/enable_spec.rb13
-rw-r--r--spec/rubyspec/core/gc/garbage_collect_spec.rb15
-rw-r--r--spec/rubyspec/core/gc/profiler/clear_spec.rb5
-rw-r--r--spec/rubyspec/core/gc/profiler/disable_spec.rb16
-rw-r--r--spec/rubyspec/core/gc/profiler/enable_spec.rb17
-rw-r--r--spec/rubyspec/core/gc/profiler/enabled_spec.rb21
-rw-r--r--spec/rubyspec/core/gc/profiler/report_spec.rb5
-rw-r--r--spec/rubyspec/core/gc/profiler/result_spec.rb7
-rw-r--r--spec/rubyspec/core/gc/profiler/total_time_spec.rb7
-rw-r--r--spec/rubyspec/core/gc/start_spec.rb8
-rw-r--r--spec/rubyspec/core/gc/stress_spec.rb27
-rw-r--r--spec/rubyspec/core/hash/allocate_spec.rb15
-rw-r--r--spec/rubyspec/core/hash/any_spec.rb30
-rw-r--r--spec/rubyspec/core/hash/assoc_spec.rb50
-rw-r--r--spec/rubyspec/core/hash/clear_spec.rb32
-rw-r--r--spec/rubyspec/core/hash/clone_spec.rb13
-rw-r--r--spec/rubyspec/core/hash/compact_spec.rb61
-rw-r--r--spec/rubyspec/core/hash/compare_by_identity_spec.rb140
-rw-r--r--spec/rubyspec/core/hash/constructor_spec.rb110
-rw-r--r--spec/rubyspec/core/hash/default_proc_spec.rb80
-rw-r--r--spec/rubyspec/core/hash/default_spec.rb46
-rw-r--r--spec/rubyspec/core/hash/delete_if_spec.rb44
-rw-r--r--spec/rubyspec/core/hash/delete_spec.rb44
-rw-r--r--spec/rubyspec/core/hash/dig_spec.rb68
-rw-r--r--spec/rubyspec/core/hash/each_key_spec.rb23
-rw-r--r--spec/rubyspec/core/hash/each_pair_spec.rb11
-rw-r--r--spec/rubyspec/core/hash/each_spec.rb11
-rw-r--r--spec/rubyspec/core/hash/each_value_spec.rb23
-rw-r--r--spec/rubyspec/core/hash/element_reference_spec.rb120
-rw-r--r--spec/rubyspec/core/hash/element_set_spec.rb7
-rw-r--r--spec/rubyspec/core/hash/empty_spec.rb15
-rw-r--r--spec/rubyspec/core/hash/eql_spec.rb9
-rw-r--r--spec/rubyspec/core/hash/equal_value_spec.rb18
-rw-r--r--spec/rubyspec/core/hash/fetch_spec.rb36
-rw-r--r--spec/rubyspec/core/hash/fetch_values_spec.rb35
-rw-r--r--spec/rubyspec/core/hash/fixtures/classes.rb68
-rw-r--r--spec/rubyspec/core/hash/flatten_spec.rb62
-rw-r--r--spec/rubyspec/core/hash/gt_spec.rb44
-rw-r--r--spec/rubyspec/core/hash/gte_spec.rb44
-rw-r--r--spec/rubyspec/core/hash/has_key_spec.rb8
-rw-r--r--spec/rubyspec/core/hash/has_value_spec.rb8
-rw-r--r--spec/rubyspec/core/hash/hash_spec.rb36
-rw-r--r--spec/rubyspec/core/hash/include_spec.rb7
-rw-r--r--spec/rubyspec/core/hash/index_spec.rb7
-rw-r--r--spec/rubyspec/core/hash/initialize_spec.rb40
-rw-r--r--spec/rubyspec/core/hash/inspect_spec.rb7
-rw-r--r--spec/rubyspec/core/hash/invert_spec.rb27
-rw-r--r--spec/rubyspec/core/hash/keep_if_spec.rb37
-rw-r--r--spec/rubyspec/core/hash/key_spec.rb12
-rw-r--r--spec/rubyspec/core/hash/keys_spec.rb23
-rw-r--r--spec/rubyspec/core/hash/length_spec.rb7
-rw-r--r--spec/rubyspec/core/hash/lt_spec.rb44
-rw-r--r--spec/rubyspec/core/hash/lte_spec.rb44
-rw-r--r--spec/rubyspec/core/hash/member_spec.rb7
-rw-r--r--spec/rubyspec/core/hash/merge_spec.rb77
-rw-r--r--spec/rubyspec/core/hash/new_spec.rb36
-rw-r--r--spec/rubyspec/core/hash/rassoc_spec.rb42
-rw-r--r--spec/rubyspec/core/hash/rehash_spec.rb42
-rw-r--r--spec/rubyspec/core/hash/reject_spec.rb100
-rw-r--r--spec/rubyspec/core/hash/replace_spec.rb7
-rw-r--r--spec/rubyspec/core/hash/select_spec.rb83
-rw-r--r--spec/rubyspec/core/hash/shared/comparison.rb15
-rw-r--r--spec/rubyspec/core/hash/shared/each.rb68
-rw-r--r--spec/rubyspec/core/hash/shared/eql.rb216
-rw-r--r--spec/rubyspec/core/hash/shared/equal.rb90
-rw-r--r--spec/rubyspec/core/hash/shared/greater_than.rb23
-rw-r--r--spec/rubyspec/core/hash/shared/index.rb27
-rw-r--r--spec/rubyspec/core/hash/shared/iteration.rb19
-rw-r--r--spec/rubyspec/core/hash/shared/key.rb38
-rw-r--r--spec/rubyspec/core/hash/shared/length.rb12
-rw-r--r--spec/rubyspec/core/hash/shared/less_than.rb23
-rw-r--r--spec/rubyspec/core/hash/shared/replace.rb51
-rw-r--r--spec/rubyspec/core/hash/shared/store.rb98
-rw-r--r--spec/rubyspec/core/hash/shared/to_s.rb69
-rw-r--r--spec/rubyspec/core/hash/shared/update.rb59
-rw-r--r--spec/rubyspec/core/hash/shared/value.rb14
-rw-r--r--spec/rubyspec/core/hash/shared/values_at.rb9
-rw-r--r--spec/rubyspec/core/hash/shift_spec.rb64
-rw-r--r--spec/rubyspec/core/hash/size_spec.rb7
-rw-r--r--spec/rubyspec/core/hash/sort_spec.rb17
-rw-r--r--spec/rubyspec/core/hash/store_spec.rb7
-rw-r--r--spec/rubyspec/core/hash/to_a_spec.rb37
-rw-r--r--spec/rubyspec/core/hash/to_h_spec.rb34
-rw-r--r--spec/rubyspec/core/hash/to_hash_spec.rb14
-rw-r--r--spec/rubyspec/core/hash/to_proc_spec.rb89
-rw-r--r--spec/rubyspec/core/hash/to_s_spec.rb7
-rw-r--r--spec/rubyspec/core/hash/transform_values_spec.rb71
-rw-r--r--spec/rubyspec/core/hash/try_convert_spec.rb50
-rw-r--r--spec/rubyspec/core/hash/update_spec.rb7
-rw-r--r--spec/rubyspec/core/hash/value_spec.rb8
-rw-r--r--spec/rubyspec/core/hash/values_at_spec.rb7
-rw-r--r--spec/rubyspec/core/hash/values_spec.rb10
-rw-r--r--spec/rubyspec/core/integer/ceil_spec.rb6
-rw-r--r--spec/rubyspec/core/integer/chr_spec.rb239
-rw-r--r--spec/rubyspec/core/integer/denominator_spec.rb20
-rw-r--r--spec/rubyspec/core/integer/downto_spec.rb69
-rw-r--r--spec/rubyspec/core/integer/even_spec.rb20
-rw-r--r--spec/rubyspec/core/integer/floor_spec.rb6
-rw-r--r--spec/rubyspec/core/integer/gcd_spec.rb58
-rw-r--r--spec/rubyspec/core/integer/gcdlcm_spec.rb53
-rw-r--r--spec/rubyspec/core/integer/integer_spec.rb15
-rw-r--r--spec/rubyspec/core/integer/lcm_spec.rb58
-rw-r--r--spec/rubyspec/core/integer/next_spec.rb6
-rw-r--r--spec/rubyspec/core/integer/numerator_spec.rb18
-rw-r--r--spec/rubyspec/core/integer/odd_spec.rb18
-rw-r--r--spec/rubyspec/core/integer/ord_spec.rb17
-rw-r--r--spec/rubyspec/core/integer/pred_spec.rb11
-rw-r--r--spec/rubyspec/core/integer/rationalize_spec.rb39
-rw-r--r--spec/rubyspec/core/integer/round_spec.rb77
-rw-r--r--spec/rubyspec/core/integer/shared/next.rb25
-rw-r--r--spec/rubyspec/core/integer/shared/to_i.rb8
-rw-r--r--spec/rubyspec/core/integer/succ_spec.rb6
-rw-r--r--spec/rubyspec/core/integer/times_spec.rb79
-rw-r--r--spec/rubyspec/core/integer/to_i_spec.rb6
-rw-r--r--spec/rubyspec/core/integer/to_int_spec.rb6
-rw-r--r--spec/rubyspec/core/integer/to_r_spec.rb26
-rw-r--r--spec/rubyspec/core/integer/truncate_spec.rb6
-rw-r--r--spec/rubyspec/core/integer/upto_spec.rb69
-rw-r--r--spec/rubyspec/core/io/advise_spec.rb96
-rw-r--r--spec/rubyspec/core/io/binmode_spec.rb60
-rw-r--r--spec/rubyspec/core/io/binread_spec.rb49
-rw-r--r--spec/rubyspec/core/io/binwrite_spec.rb8
-rw-r--r--spec/rubyspec/core/io/bytes_spec.rb43
-rw-r--r--spec/rubyspec/core/io/chars_spec.rb12
-rw-r--r--spec/rubyspec/core/io/close_on_exec_spec.rb121
-rw-r--r--spec/rubyspec/core/io/close_read_spec.rb80
-rw-r--r--spec/rubyspec/core/io/close_spec.rb82
-rw-r--r--spec/rubyspec/core/io/close_write_spec.rb84
-rw-r--r--spec/rubyspec/core/io/closed_spec.rb20
-rw-r--r--spec/rubyspec/core/io/codepoints_spec.rb25
-rw-r--r--spec/rubyspec/core/io/constants_spec.rb19
-rw-r--r--spec/rubyspec/core/io/copy_stream_spec.rb282
-rw-r--r--spec/rubyspec/core/io/dup_spec.rb69
-rw-r--r--spec/rubyspec/core/io/each_byte_spec.rb57
-rw-r--r--spec/rubyspec/core/io/each_char_spec.rb12
-rw-r--r--spec/rubyspec/core/io/each_codepoint_spec.rb45
-rw-r--r--spec/rubyspec/core/io/each_line_spec.rb11
-rw-r--r--spec/rubyspec/core/io/each_spec.rb11
-rw-r--r--spec/rubyspec/core/io/eof_spec.rb107
-rw-r--r--spec/rubyspec/core/io/external_encoding_spec.rb218
-rw-r--r--spec/rubyspec/core/io/fcntl_spec.rb8
-rw-r--r--spec/rubyspec/core/io/fdatasync_spec.rb5
-rw-r--r--spec/rubyspec/core/io/fileno_spec.rb12
-rw-r--r--spec/rubyspec/core/io/fixtures/bom_UTF-16BE.txtbin0 -> 20 bytes
-rw-r--r--spec/rubyspec/core/io/fixtures/bom_UTF-16LE.txtbin0 -> 20 bytes
-rw-r--r--spec/rubyspec/core/io/fixtures/bom_UTF-32BE.txtbin0 -> 40 bytes
-rw-r--r--spec/rubyspec/core/io/fixtures/bom_UTF-32LE.txtbin0 -> 40 bytes
-rw-r--r--spec/rubyspec/core/io/fixtures/bom_UTF-8.txt1
-rw-r--r--spec/rubyspec/core/io/fixtures/classes.rb178
-rw-r--r--spec/rubyspec/core/io/fixtures/copy_stream.txt6
-rw-r--r--spec/rubyspec/core/io/fixtures/empty.txt0
-rw-r--r--spec/rubyspec/core/io/fixtures/incomplete.txt1
-rw-r--r--spec/rubyspec/core/io/fixtures/lines.txt9
-rw-r--r--spec/rubyspec/core/io/fixtures/no_bom_UTF-8.txt1
-rw-r--r--spec/rubyspec/core/io/fixtures/numbered_lines.txt5
-rw-r--r--spec/rubyspec/core/io/fixtures/one_byte.txt1
-rw-r--r--spec/rubyspec/core/io/fixtures/read_binary.txt1
-rw-r--r--spec/rubyspec/core/io/fixtures/read_euc_jp.txt1
-rw-r--r--spec/rubyspec/core/io/fixtures/read_text.txt1
-rw-r--r--spec/rubyspec/core/io/fixtures/reopen_stdout.rb3
-rw-r--r--spec/rubyspec/core/io/flush_spec.rb8
-rw-r--r--spec/rubyspec/core/io/for_fd_spec.rb10
-rw-r--r--spec/rubyspec/core/io/foreach_spec.rb81
-rw-r--r--spec/rubyspec/core/io/fsync_spec.rb24
-rw-r--r--spec/rubyspec/core/io/getbyte_spec.rb42
-rw-r--r--spec/rubyspec/core/io/getc_spec.rb42
-rw-r--r--spec/rubyspec/core/io/gets_spec.rb313
-rw-r--r--spec/rubyspec/core/io/initialize_spec.rb53
-rw-r--r--spec/rubyspec/core/io/inspect_spec.rb23
-rw-r--r--spec/rubyspec/core/io/internal_encoding_spec.rb140
-rw-r--r--spec/rubyspec/core/io/io_spec.rb11
-rw-r--r--spec/rubyspec/core/io/ioctl_spec.rb32
-rw-r--r--spec/rubyspec/core/io/isatty_spec.rb6
-rw-r--r--spec/rubyspec/core/io/lineno_spec.rb95
-rw-r--r--spec/rubyspec/core/io/lines_spec.rb42
-rw-r--r--spec/rubyspec/core/io/new_spec.rb10
-rw-r--r--spec/rubyspec/core/io/open_spec.rb86
-rw-r--r--spec/rubyspec/core/io/output_spec.rb27
-rw-r--r--spec/rubyspec/core/io/pid_spec.rb35
-rw-r--r--spec/rubyspec/core/io/pipe_spec.rb214
-rw-r--r--spec/rubyspec/core/io/popen_spec.rb286
-rw-r--r--spec/rubyspec/core/io/pos_spec.rb12
-rw-r--r--spec/rubyspec/core/io/print_spec.rb54
-rw-r--r--spec/rubyspec/core/io/printf_spec.rb32
-rw-r--r--spec/rubyspec/core/io/putc_spec.rb11
-rw-r--r--spec/rubyspec/core/io/puts_spec.rb144
-rw-r--r--spec/rubyspec/core/io/read_nonblock_spec.rb85
-rw-r--r--spec/rubyspec/core/io/read_spec.rb616
-rw-r--r--spec/rubyspec/core/io/readbyte_spec.rb26
-rw-r--r--spec/rubyspec/core/io/readchar_spec.rb44
-rw-r--r--spec/rubyspec/core/io/readline_spec.rb45
-rw-r--r--spec/rubyspec/core/io/readlines_spec.rb210
-rw-r--r--spec/rubyspec/core/io/readpartial_spec.rb96
-rw-r--r--spec/rubyspec/core/io/reopen_spec.rb302
-rw-r--r--spec/rubyspec/core/io/rewind_spec.rb38
-rw-r--r--spec/rubyspec/core/io/seek_spec.rb79
-rw-r--r--spec/rubyspec/core/io/select_spec.rb115
-rw-r--r--spec/rubyspec/core/io/set_encoding_spec.rb193
-rw-r--r--spec/rubyspec/core/io/shared/binwrite.rb78
-rw-r--r--spec/rubyspec/core/io/shared/chars.rb73
-rw-r--r--spec/rubyspec/core/io/shared/codepoints.rb54
-rw-r--r--spec/rubyspec/core/io/shared/each.rb135
-rw-r--r--spec/rubyspec/core/io/shared/gets_ascii.rb19
-rw-r--r--spec/rubyspec/core/io/shared/new.rb378
-rw-r--r--spec/rubyspec/core/io/shared/pos.rb72
-rw-r--r--spec/rubyspec/core/io/shared/readlines.rb204
-rw-r--r--spec/rubyspec/core/io/shared/tty.rb25
-rw-r--r--spec/rubyspec/core/io/shared/write.rb72
-rw-r--r--spec/rubyspec/core/io/stat_spec.rb24
-rw-r--r--spec/rubyspec/core/io/sync_spec.rb64
-rw-r--r--spec/rubyspec/core/io/sysopen_spec.rb50
-rw-r--r--spec/rubyspec/core/io/sysread_spec.rb82
-rw-r--r--spec/rubyspec/core/io/sysseek_spec.rb44
-rw-r--r--spec/rubyspec/core/io/syswrite_spec.rb54
-rw-r--r--spec/rubyspec/core/io/tell_spec.rb7
-rw-r--r--spec/rubyspec/core/io/to_i_spec.rb12
-rw-r--r--spec/rubyspec/core/io/to_io_spec.rb21
-rw-r--r--spec/rubyspec/core/io/try_convert_spec.rb49
-rw-r--r--spec/rubyspec/core/io/tty_spec.rb6
-rw-r--r--spec/rubyspec/core/io/ungetbyte_spec.rb48
-rw-r--r--spec/rubyspec/core/io/ungetc_spec.rb119
-rw-r--r--spec/rubyspec/core/io/write_nonblock_spec.rb76
-rw-r--r--spec/rubyspec/core/io/write_spec.rb157
-rw-r--r--spec/rubyspec/core/kernel/Array_spec.rb97
-rw-r--r--spec/rubyspec/core/kernel/Complex_spec.rb6
-rw-r--r--spec/rubyspec/core/kernel/Float_spec.rb316
-rw-r--r--spec/rubyspec/core/kernel/Hash_spec.rb57
-rw-r--r--spec/rubyspec/core/kernel/Integer_spec.rb697
-rw-r--r--spec/rubyspec/core/kernel/Rational_spec.rb6
-rw-r--r--spec/rubyspec/core/kernel/String_spec.rb106
-rw-r--r--spec/rubyspec/core/kernel/__callee___spec.rb48
-rw-r--r--spec/rubyspec/core/kernel/__dir___spec.rb13
-rw-r--r--spec/rubyspec/core/kernel/__method___spec.rb40
-rw-r--r--spec/rubyspec/core/kernel/abort_spec.rb15
-rw-r--r--spec/rubyspec/core/kernel/at_exit_spec.rb44
-rw-r--r--spec/rubyspec/core/kernel/autoload_spec.rb122
-rw-r--r--spec/rubyspec/core/kernel/backtick_spec.rb80
-rw-r--r--spec/rubyspec/core/kernel/binding_spec.rb51
-rw-r--r--spec/rubyspec/core/kernel/block_given_spec.rb38
-rw-r--r--spec/rubyspec/core/kernel/caller_locations_spec.rb32
-rw-r--r--spec/rubyspec/core/kernel/caller_spec.rb31
-rw-r--r--spec/rubyspec/core/kernel/case_compare_spec.rb135
-rw-r--r--spec/rubyspec/core/kernel/catch_spec.rb127
-rw-r--r--spec/rubyspec/core/kernel/chomp_spec.rb67
-rw-r--r--spec/rubyspec/core/kernel/chop_spec.rb55
-rw-r--r--spec/rubyspec/core/kernel/class_spec.rb26
-rw-r--r--spec/rubyspec/core/kernel/clone_spec.rb108
-rw-r--r--spec/rubyspec/core/kernel/comparison_spec.rb31
-rw-r--r--spec/rubyspec/core/kernel/define_singleton_method_spec.rb101
-rw-r--r--spec/rubyspec/core/kernel/display_spec.rb6
-rw-r--r--spec/rubyspec/core/kernel/dup_spec.rb67
-rw-r--r--spec/rubyspec/core/kernel/enum_for_spec.rb5
-rw-r--r--spec/rubyspec/core/kernel/eql_spec.rb11
-rw-r--r--spec/rubyspec/core/kernel/equal_value_spec.rb15
-rw-r--r--spec/rubyspec/core/kernel/eval_spec.rb216
-rw-r--r--spec/rubyspec/core/kernel/exec_spec.rb18
-rw-r--r--spec/rubyspec/core/kernel/exit_spec.rb27
-rw-r--r--spec/rubyspec/core/kernel/extend_spec.rb79
-rw-r--r--spec/rubyspec/core/kernel/fail_spec.rb43
-rw-r--r--spec/rubyspec/core/kernel/fixtures/__callee__.rb34
-rw-r--r--spec/rubyspec/core/kernel/fixtures/__method__.rb34
-rw-r--r--spec/rubyspec/core/kernel/fixtures/autoload_b.rb5
-rw-r--r--spec/rubyspec/core/kernel/fixtures/autoload_c.rb5
-rw-r--r--spec/rubyspec/core/kernel/fixtures/autoload_d.rb5
-rw-r--r--spec/rubyspec/core/kernel/fixtures/autoload_frozen.rb7
-rw-r--r--spec/rubyspec/core/kernel/fixtures/caller.rb7
-rw-r--r--spec/rubyspec/core/kernel/fixtures/caller_locations.rb7
-rw-r--r--spec/rubyspec/core/kernel/fixtures/chomp.rb4
-rw-r--r--spec/rubyspec/core/kernel/fixtures/chomp_f.rb4
-rw-r--r--spec/rubyspec/core/kernel/fixtures/chop.rb4
-rw-r--r--spec/rubyspec/core/kernel/fixtures/chop_f.rb4
-rw-r--r--spec/rubyspec/core/kernel/fixtures/classes.rb425
-rw-r--r--spec/rubyspec/core/kernel/fixtures/eval_locals.rb6
-rw-r--r--spec/rubyspec/core/kernel/fixtures/eval_return_with_lambda.rb12
-rw-r--r--spec/rubyspec/core/kernel/fixtures/eval_return_without_lambda.rb14
-rw-r--r--spec/rubyspec/core/kernel/fixtures/test.rb362
-rw-r--r--spec/rubyspec/core/kernel/fork_spec.rb15
-rw-r--r--spec/rubyspec/core/kernel/format_spec.rb14
-rw-r--r--spec/rubyspec/core/kernel/freeze_spec.rb67
-rw-r--r--spec/rubyspec/core/kernel/frozen_spec.rb52
-rw-r--r--spec/rubyspec/core/kernel/gets_spec.rb12
-rw-r--r--spec/rubyspec/core/kernel/global_variables_spec.rb26
-rw-r--r--spec/rubyspec/core/kernel/gsub_spec.rb96
-rw-r--r--spec/rubyspec/core/kernel/inspect_spec.rb31
-rw-r--r--spec/rubyspec/core/kernel/instance_of_spec.rb40
-rw-r--r--spec/rubyspec/core/kernel/instance_variable_defined_spec.rb41
-rw-r--r--spec/rubyspec/core/kernel/instance_variable_get_spec.rb105
-rw-r--r--spec/rubyspec/core/kernel/instance_variable_set_spec.rb93
-rw-r--r--spec/rubyspec/core/kernel/instance_variables_spec.rb27
-rw-r--r--spec/rubyspec/core/kernel/is_a_spec.rb6
-rw-r--r--spec/rubyspec/core/kernel/iterator_spec.rb12
-rw-r--r--spec/rubyspec/core/kernel/itself_spec.rb10
-rw-r--r--spec/rubyspec/core/kernel/kind_of_spec.rb6
-rw-r--r--spec/rubyspec/core/kernel/lambda_spec.rb86
-rw-r--r--spec/rubyspec/core/kernel/load_spec.rb40
-rw-r--r--spec/rubyspec/core/kernel/local_variables_spec.rb37
-rw-r--r--spec/rubyspec/core/kernel/loop_spec.rb81
-rw-r--r--spec/rubyspec/core/kernel/match_spec.rb14
-rw-r--r--spec/rubyspec/core/kernel/method_spec.rb37
-rw-r--r--spec/rubyspec/core/kernel/methods_spec.rb101
-rw-r--r--spec/rubyspec/core/kernel/nil_spec.rb6
-rw-r--r--spec/rubyspec/core/kernel/not_match_spec.rb21
-rw-r--r--spec/rubyspec/core/kernel/object_id_spec.rb6
-rw-r--r--spec/rubyspec/core/kernel/open_spec.rb141
-rw-r--r--spec/rubyspec/core/kernel/p_spec.rb79
-rw-r--r--spec/rubyspec/core/kernel/print_spec.rb12
-rw-r--r--spec/rubyspec/core/kernel/printf_spec.rb34
-rw-r--r--spec/rubyspec/core/kernel/private_methods_spec.rb69
-rw-r--r--spec/rubyspec/core/kernel/proc_spec.rb50
-rw-r--r--spec/rubyspec/core/kernel/protected_methods_spec.rb69
-rw-r--r--spec/rubyspec/core/kernel/public_method_spec.rb32
-rw-r--r--spec/rubyspec/core/kernel/public_methods_spec.rb76
-rw-r--r--spec/rubyspec/core/kernel/public_send_spec.rb108
-rw-r--r--spec/rubyspec/core/kernel/putc_spec.rb39
-rw-r--r--spec/rubyspec/core/kernel/puts_spec.rb29
-rw-r--r--spec/rubyspec/core/kernel/raise_spec.rb17
-rw-r--r--spec/rubyspec/core/kernel/rand_spec.rb139
-rw-r--r--spec/rubyspec/core/kernel/readline_spec.rb12
-rw-r--r--spec/rubyspec/core/kernel/readlines_spec.rb12
-rw-r--r--spec/rubyspec/core/kernel/remove_instance_variable_spec.rb59
-rw-r--r--spec/rubyspec/core/kernel/require_relative_spec.rb349
-rw-r--r--spec/rubyspec/core/kernel/require_spec.rb36
-rw-r--r--spec/rubyspec/core/kernel/respond_to_missing_spec.rb100
-rw-r--r--spec/rubyspec/core/kernel/respond_to_spec.rb73
-rw-r--r--spec/rubyspec/core/kernel/select_spec.rb20
-rw-r--r--spec/rubyspec/core/kernel/send_spec.rb68
-rw-r--r--spec/rubyspec/core/kernel/set_trace_func_spec.rb12
-rw-r--r--spec/rubyspec/core/kernel/shared/dup_clone.rb125
-rw-r--r--spec/rubyspec/core/kernel/shared/kind_of.rb44
-rw-r--r--spec/rubyspec/core/kernel/shared/lambda.rb9
-rw-r--r--spec/rubyspec/core/kernel/shared/load.rb139
-rw-r--r--spec/rubyspec/core/kernel/shared/method.rb50
-rw-r--r--spec/rubyspec/core/kernel/shared/require.rb703
-rw-r--r--spec/rubyspec/core/kernel/singleton_class_spec.rb27
-rw-r--r--spec/rubyspec/core/kernel/singleton_methods_spec.rb180
-rw-r--r--spec/rubyspec/core/kernel/sleep_spec.rb52
-rw-r--r--spec/rubyspec/core/kernel/spawn_spec.rb25
-rw-r--r--spec/rubyspec/core/kernel/sprintf_spec.rb310
-rw-r--r--spec/rubyspec/core/kernel/srand_spec.rb61
-rw-r--r--spec/rubyspec/core/kernel/sub_spec.rb26
-rw-r--r--spec/rubyspec/core/kernel/syscall_spec.rb12
-rw-r--r--spec/rubyspec/core/kernel/system_spec.rb107
-rw-r--r--spec/rubyspec/core/kernel/taint_spec.rb45
-rw-r--r--spec/rubyspec/core/kernel/tainted_spec.rb12
-rw-r--r--spec/rubyspec/core/kernel/tap_spec.rb13
-rw-r--r--spec/rubyspec/core/kernel/test_spec.rb98
-rw-r--r--spec/rubyspec/core/kernel/throw_spec.rb80
-rw-r--r--spec/rubyspec/core/kernel/to_enum_spec.rb5
-rw-r--r--spec/rubyspec/core/kernel/to_s_spec.rb16
-rw-r--r--spec/rubyspec/core/kernel/trace_var_spec.rb54
-rw-r--r--spec/rubyspec/core/kernel/trap_spec.rb12
-rw-r--r--spec/rubyspec/core/kernel/trust_spec.rb25
-rw-r--r--spec/rubyspec/core/kernel/untaint_spec.rb25
-rw-r--r--spec/rubyspec/core/kernel/untrace_var_spec.rb12
-rw-r--r--spec/rubyspec/core/kernel/untrust_spec.rb25
-rw-r--r--spec/rubyspec/core/kernel/untrusted_spec.rb28
-rw-r--r--spec/rubyspec/core/kernel/warn_spec.rb79
-rw-r--r--spec/rubyspec/core/main/define_method_spec.rb28
-rw-r--r--spec/rubyspec/core/main/fixtures/classes.rb15
-rw-r--r--spec/rubyspec/core/main/fixtures/wrapped_include.rb1
-rw-r--r--spec/rubyspec/core/main/include_spec.rb16
-rw-r--r--spec/rubyspec/core/main/private_spec.rb23
-rw-r--r--spec/rubyspec/core/main/public_spec.rb23
-rw-r--r--spec/rubyspec/core/main/to_s_spec.rb7
-rw-r--r--spec/rubyspec/core/marshal/dump_spec.rb573
-rw-r--r--spec/rubyspec/core/marshal/fixtures/marshal_data.rb420
-rw-r--r--spec/rubyspec/core/marshal/fixtures/random.dumpbin0 -> 2520 bytes
-rw-r--r--spec/rubyspec/core/marshal/float_spec.rb77
-rw-r--r--spec/rubyspec/core/marshal/load_spec.rb6
-rw-r--r--spec/rubyspec/core/marshal/major_version_spec.rb7
-rw-r--r--spec/rubyspec/core/marshal/minor_version_spec.rb7
-rw-r--r--spec/rubyspec/core/marshal/restore_spec.rb6
-rw-r--r--spec/rubyspec/core/marshal/shared/load.rb830
-rw-r--r--spec/rubyspec/core/matchdata/begin_spec.rb30
-rw-r--r--spec/rubyspec/core/matchdata/captures_spec.rb7
-rw-r--r--spec/rubyspec/core/matchdata/element_reference_spec.rb87
-rw-r--r--spec/rubyspec/core/matchdata/end_spec.rb30
-rw-r--r--spec/rubyspec/core/matchdata/eql_spec.rb6
-rw-r--r--spec/rubyspec/core/matchdata/equal_value_spec.rb6
-rw-r--r--spec/rubyspec/core/matchdata/hash_spec.rb5
-rw-r--r--spec/rubyspec/core/matchdata/inspect_spec.rb17
-rw-r--r--spec/rubyspec/core/matchdata/length_spec.rb6
-rw-r--r--spec/rubyspec/core/matchdata/names_spec.rb33
-rw-r--r--spec/rubyspec/core/matchdata/offset_spec.rb30
-rw-r--r--spec/rubyspec/core/matchdata/post_match_spec.rb36
-rw-r--r--spec/rubyspec/core/matchdata/pre_match_spec.rb36
-rw-r--r--spec/rubyspec/core/matchdata/regexp_spec.rb13
-rw-r--r--spec/rubyspec/core/matchdata/shared/eql.rb26
-rw-r--r--spec/rubyspec/core/matchdata/shared/length.rb5
-rw-r--r--spec/rubyspec/core/matchdata/size_spec.rb6
-rw-r--r--spec/rubyspec/core/matchdata/string_spec.rb14
-rw-r--r--spec/rubyspec/core/matchdata/to_a_spec.rb7
-rw-r--r--spec/rubyspec/core/matchdata/to_s_spec.rb7
-rw-r--r--spec/rubyspec/core/matchdata/values_at_spec.rb13
-rw-r--r--spec/rubyspec/core/math/acos_spec.rb58
-rw-r--r--spec/rubyspec/core/math/acosh_spec.rb43
-rw-r--r--spec/rubyspec/core/math/asin_spec.rb50
-rw-r--r--spec/rubyspec/core/math/asinh_spec.rb42
-rw-r--r--spec/rubyspec/core/math/atan2_spec.rb54
-rw-r--r--spec/rubyspec/core/math/atan_spec.rb40
-rw-r--r--spec/rubyspec/core/math/atanh_spec.rb14
-rw-r--r--spec/rubyspec/core/math/cbrt_spec.rb27
-rw-r--r--spec/rubyspec/core/math/constants_spec.rb22
-rw-r--r--spec/rubyspec/core/math/cos_spec.rb42
-rw-r--r--spec/rubyspec/core/math/cosh_spec.rb37
-rw-r--r--spec/rubyspec/core/math/erf_spec.rb44
-rw-r--r--spec/rubyspec/core/math/erfc_spec.rb43
-rw-r--r--spec/rubyspec/core/math/exp_spec.rb37
-rw-r--r--spec/rubyspec/core/math/fixtures/classes.rb28
-rw-r--r--spec/rubyspec/core/math/frexp_spec.rb37
-rw-r--r--spec/rubyspec/core/math/gamma_spec.rb69
-rw-r--r--spec/rubyspec/core/math/hypot_spec.rb41
-rw-r--r--spec/rubyspec/core/math/ldexp_spec.rb54
-rw-r--r--spec/rubyspec/core/math/lgamma_spec.rb56
-rw-r--r--spec/rubyspec/core/math/log10_spec.rb45
-rw-r--r--spec/rubyspec/core/math/log2_spec.rb41
-rw-r--r--spec/rubyspec/core/math/log_spec.rb59
-rw-r--r--spec/rubyspec/core/math/sin_spec.rb39
-rw-r--r--spec/rubyspec/core/math/sinh_spec.rb37
-rw-r--r--spec/rubyspec/core/math/sqrt_spec.rb36
-rw-r--r--spec/rubyspec/core/math/tan_spec.rb42
-rw-r--r--spec/rubyspec/core/math/tanh_spec.rb39
-rw-r--r--spec/rubyspec/core/method/arity_spec.rb222
-rw-r--r--spec/rubyspec/core/method/call_spec.rb7
-rw-r--r--spec/rubyspec/core/method/clone_spec.rb14
-rw-r--r--spec/rubyspec/core/method/curry_spec.rb36
-rw-r--r--spec/rubyspec/core/method/element_reference_spec.rb7
-rw-r--r--spec/rubyspec/core/method/eql_spec.rb6
-rw-r--r--spec/rubyspec/core/method/equal_value_spec.rb6
-rw-r--r--spec/rubyspec/core/method/fixtures/classes.rb184
-rw-r--r--spec/rubyspec/core/method/hash_spec.rb17
-rw-r--r--spec/rubyspec/core/method/inspect_spec.rb6
-rw-r--r--spec/rubyspec/core/method/name_spec.rb22
-rw-r--r--spec/rubyspec/core/method/owner_spec.rb26
-rw-r--r--spec/rubyspec/core/method/parameters_spec.rb244
-rw-r--r--spec/rubyspec/core/method/receiver_spec.rb22
-rw-r--r--spec/rubyspec/core/method/shared/call.rb51
-rw-r--r--spec/rubyspec/core/method/shared/eql.rb94
-rw-r--r--spec/rubyspec/core/method/shared/to_s.rb34
-rw-r--r--spec/rubyspec/core/method/source_location_spec.rb95
-rw-r--r--spec/rubyspec/core/method/super_method_spec.rb45
-rw-r--r--spec/rubyspec/core/method/to_proc_spec.rb89
-rw-r--r--spec/rubyspec/core/method/to_s_spec.rb6
-rw-r--r--spec/rubyspec/core/method/unbind_spec.rb37
-rw-r--r--spec/rubyspec/core/module/alias_method_spec.rb150
-rw-r--r--spec/rubyspec/core/module/allocate_spec.rb14
-rw-r--r--spec/rubyspec/core/module/ancestors_spec.rb70
-rw-r--r--spec/rubyspec/core/module/append_features_spec.rb73
-rw-r--r--spec/rubyspec/core/module/attr_accessor_spec.rb90
-rw-r--r--spec/rubyspec/core/module/attr_reader_spec.rb64
-rw-r--r--spec/rubyspec/core/module/attr_spec.rb149
-rw-r--r--spec/rubyspec/core/module/attr_writer_spec.rb64
-rw-r--r--spec/rubyspec/core/module/autoload_spec.rb462
-rw-r--r--spec/rubyspec/core/module/case_compare_spec.rb31
-rw-r--r--spec/rubyspec/core/module/class_eval_spec.rb7
-rw-r--r--spec/rubyspec/core/module/class_exec_spec.rb7
-rw-r--r--spec/rubyspec/core/module/class_variable_defined_spec.rb72
-rw-r--r--spec/rubyspec/core/module/class_variable_get_spec.rb76
-rw-r--r--spec/rubyspec/core/module/class_variable_set_spec.rb62
-rw-r--r--spec/rubyspec/core/module/class_variables_spec.rb26
-rw-r--r--spec/rubyspec/core/module/comparison_spec.rb36
-rw-r--r--spec/rubyspec/core/module/const_defined_spec.rb144
-rw-r--r--spec/rubyspec/core/module/const_get_spec.rb208
-rw-r--r--spec/rubyspec/core/module/const_missing_spec.rb27
-rw-r--r--spec/rubyspec/core/module/const_set_spec.rb86
-rw-r--r--spec/rubyspec/core/module/constants_spec.rb91
-rw-r--r--spec/rubyspec/core/module/define_method_spec.rb626
-rw-r--r--spec/rubyspec/core/module/define_singleton_method_spec.rb17
-rw-r--r--spec/rubyspec/core/module/deprecate_constant_spec.rb52
-rw-r--r--spec/rubyspec/core/module/eql_spec.rb7
-rw-r--r--spec/rubyspec/core/module/equal_spec.rb7
-rw-r--r--spec/rubyspec/core/module/equal_value_spec.rb7
-rw-r--r--spec/rubyspec/core/module/extend_object_spec.rb68
-rw-r--r--spec/rubyspec/core/module/extended_spec.rb44
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload.rb1
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_abc.rb11
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_c.rb11
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_concur.rb9
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_d.rb11
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_e.rb7
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_empty.rb1
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_ex1.rb16
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_f.rb7
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_g.rb7
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_h.rb7
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_i.rb5
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_j.rb3
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_k.rb7
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_lm.rb4
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_o.rb1
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_r.rb4
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_s.rb5
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_scope.rb8
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_subclass.rb11
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_t.rb3
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_v.rb7
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_w.rb2
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_w2.rb1
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_x.rb3
-rw-r--r--spec/rubyspec/core/module/fixtures/autoload_z.rb5
-rw-r--r--spec/rubyspec/core/module/fixtures/classes.rb605
-rw-r--r--spec/rubyspec/core/module/fixtures/constant_unicode.rb5
-rw-r--r--spec/rubyspec/core/module/fixtures/module.rb4
-rw-r--r--spec/rubyspec/core/module/fixtures/name.rb10
-rw-r--r--spec/rubyspec/core/module/fixtures/path1/load_path.rb9
-rw-r--r--spec/rubyspec/core/module/fixtures/path2/load_path.rb0
-rw-r--r--spec/rubyspec/core/module/fixtures/repeated_concurrent_autoload.rb8
-rw-r--r--spec/rubyspec/core/module/freeze_spec.rb6
-rw-r--r--spec/rubyspec/core/module/gt_spec.rb36
-rw-r--r--spec/rubyspec/core/module/gte_spec.rb33
-rw-r--r--spec/rubyspec/core/module/include_spec.rb270
-rw-r--r--spec/rubyspec/core/module/included_modules_spec.rb12
-rw-r--r--spec/rubyspec/core/module/included_spec.rb44
-rw-r--r--spec/rubyspec/core/module/initialize_copy_spec.rb10
-rw-r--r--spec/rubyspec/core/module/initialize_spec.rb18
-rw-r--r--spec/rubyspec/core/module/instance_method_spec.rb85
-rw-r--r--spec/rubyspec/core/module/instance_methods_spec.rb61
-rw-r--r--spec/rubyspec/core/module/lt_spec.rb36
-rw-r--r--spec/rubyspec/core/module/lte_spec.rb33
-rw-r--r--spec/rubyspec/core/module/method_added_spec.rb62
-rw-r--r--spec/rubyspec/core/module/method_defined_spec.rb49
-rw-r--r--spec/rubyspec/core/module/method_removed_spec.rb33
-rw-r--r--spec/rubyspec/core/module/method_undefined_spec.rb33
-rw-r--r--spec/rubyspec/core/module/module_eval_spec.rb7
-rw-r--r--spec/rubyspec/core/module/module_exec_spec.rb7
-rw-r--r--spec/rubyspec/core/module/module_function_spec.rb277
-rw-r--r--spec/rubyspec/core/module/name_spec.rb68
-rw-r--r--spec/rubyspec/core/module/nesting_spec.rb31
-rw-r--r--spec/rubyspec/core/module/new_spec.rb31
-rw-r--r--spec/rubyspec/core/module/prepend_features_spec.rb76
-rw-r--r--spec/rubyspec/core/module/prepend_spec.rb345
-rw-r--r--spec/rubyspec/core/module/prepended_spec.rb25
-rw-r--r--spec/rubyspec/core/module/private_class_method_spec.rb81
-rw-r--r--spec/rubyspec/core/module/private_constant_spec.rb32
-rw-r--r--spec/rubyspec/core/module/private_instance_methods_spec.rb54
-rw-r--r--spec/rubyspec/core/module/private_method_defined_spec.rb72
-rw-r--r--spec/rubyspec/core/module/private_spec.rb54
-rw-r--r--spec/rubyspec/core/module/protected_instance_methods_spec.rb57
-rw-r--r--spec/rubyspec/core/module/protected_method_defined_spec.rb72
-rw-r--r--spec/rubyspec/core/module/protected_spec.rb56
-rw-r--r--spec/rubyspec/core/module/public_class_method_spec.rb80
-rw-r--r--spec/rubyspec/core/module/public_constant_spec.rb38
-rw-r--r--spec/rubyspec/core/module/public_instance_method_spec.rb65
-rw-r--r--spec/rubyspec/core/module/public_instance_methods_spec.rb61
-rw-r--r--spec/rubyspec/core/module/public_method_defined_spec.rb72
-rw-r--r--spec/rubyspec/core/module/public_spec.rb44
-rw-r--r--spec/rubyspec/core/module/remove_class_variable_spec.rb44
-rw-r--r--spec/rubyspec/core/module/remove_const_spec.rb84
-rw-r--r--spec/rubyspec/core/module/remove_method_spec.rb109
-rw-r--r--spec/rubyspec/core/module/shared/class_eval.rb115
-rw-r--r--spec/rubyspec/core/module/shared/class_exec.rb29
-rw-r--r--spec/rubyspec/core/module/shared/equal_value.rb14
-rw-r--r--spec/rubyspec/core/module/shared/set_visibility.rb135
-rw-r--r--spec/rubyspec/core/module/singleton_class_spec.rb27
-rw-r--r--spec/rubyspec/core/module/to_s_spec.rb18
-rw-r--r--spec/rubyspec/core/module/undef_method_spec.rb152
-rw-r--r--spec/rubyspec/core/mutex/lock_spec.rb46
-rw-r--r--spec/rubyspec/core/mutex/locked_spec.rb36
-rw-r--r--spec/rubyspec/core/mutex/owned_spec.rb43
-rw-r--r--spec/rubyspec/core/mutex/sleep_spec.rb74
-rw-r--r--spec/rubyspec/core/mutex/synchronize_spec.rb27
-rw-r--r--spec/rubyspec/core/mutex/try_lock_spec.rb32
-rw-r--r--spec/rubyspec/core/mutex/unlock_spec.rb38
-rw-r--r--spec/rubyspec/core/nil/and_spec.rb11
-rw-r--r--spec/rubyspec/core/nil/inspect_spec.rb7
-rw-r--r--spec/rubyspec/core/nil/nil_spec.rb7
-rw-r--r--spec/rubyspec/core/nil/or_spec.rb11
-rw-r--r--spec/rubyspec/core/nil/rationalize_spec.rb16
-rw-r--r--spec/rubyspec/core/nil/to_a_spec.rb7
-rw-r--r--spec/rubyspec/core/nil/to_c_spec.rb7
-rw-r--r--spec/rubyspec/core/nil/to_f_spec.rb11
-rw-r--r--spec/rubyspec/core/nil/to_h_spec.rb8
-rw-r--r--spec/rubyspec/core/nil/to_i_spec.rb11
-rw-r--r--spec/rubyspec/core/nil/to_r_spec.rb7
-rw-r--r--spec/rubyspec/core/nil/to_s_spec.rb7
-rw-r--r--spec/rubyspec/core/nil/xor_spec.rb11
-rw-r--r--spec/rubyspec/core/numeric/abs2_spec.rb34
-rw-r--r--spec/rubyspec/core/numeric/abs_spec.rb5
-rw-r--r--spec/rubyspec/core/numeric/angle_spec.rb6
-rw-r--r--spec/rubyspec/core/numeric/arg_spec.rb6
-rw-r--r--spec/rubyspec/core/numeric/ceil_spec.rb15
-rw-r--r--spec/rubyspec/core/numeric/coerce_spec.rb76
-rw-r--r--spec/rubyspec/core/numeric/comparison_spec.rb48
-rw-r--r--spec/rubyspec/core/numeric/conj_spec.rb6
-rw-r--r--spec/rubyspec/core/numeric/conjugate_spec.rb6
-rw-r--r--spec/rubyspec/core/numeric/denominator_spec.rb24
-rw-r--r--spec/rubyspec/core/numeric/div_spec.rb22
-rw-r--r--spec/rubyspec/core/numeric/divmod_spec.rb15
-rw-r--r--spec/rubyspec/core/numeric/eql_spec.rb22
-rw-r--r--spec/rubyspec/core/numeric/fdiv_spec.rb32
-rw-r--r--spec/rubyspec/core/numeric/fixtures/classes.rb17
-rw-r--r--spec/rubyspec/core/numeric/floor_spec.rb14
-rw-r--r--spec/rubyspec/core/numeric/i_spec.rb15
-rw-r--r--spec/rubyspec/core/numeric/imag_spec.rb6
-rw-r--r--spec/rubyspec/core/numeric/imaginary_spec.rb6
-rw-r--r--spec/rubyspec/core/numeric/integer_spec.rb8
-rw-r--r--spec/rubyspec/core/numeric/magnitude_spec.rb5
-rw-r--r--spec/rubyspec/core/numeric/modulo_spec.rb24
-rw-r--r--spec/rubyspec/core/numeric/negative_spec.rb43
-rw-r--r--spec/rubyspec/core/numeric/nonzero_spec.rb18
-rw-r--r--spec/rubyspec/core/numeric/numerator_spec.rb33
-rw-r--r--spec/rubyspec/core/numeric/numeric_spec.rb7
-rw-r--r--spec/rubyspec/core/numeric/phase_spec.rb6
-rw-r--r--spec/rubyspec/core/numeric/polar_spec.rb6
-rw-r--r--spec/rubyspec/core/numeric/positive_spec.rb43
-rw-r--r--spec/rubyspec/core/numeric/quo_spec.rb55
-rw-r--r--spec/rubyspec/core/numeric/real_spec.rb13
-rw-r--r--spec/rubyspec/core/numeric/rect_spec.rb6
-rw-r--r--spec/rubyspec/core/numeric/rectangular_spec.rb6
-rw-r--r--spec/rubyspec/core/numeric/remainder_spec.rb67
-rw-r--r--spec/rubyspec/core/numeric/round_spec.rb14
-rw-r--r--spec/rubyspec/core/numeric/shared/abs.rb19
-rw-r--r--spec/rubyspec/core/numeric/shared/quo.rb7
-rw-r--r--spec/rubyspec/core/numeric/shared/rect.rb48
-rw-r--r--spec/rubyspec/core/numeric/shared/step.rb413
-rw-r--r--spec/rubyspec/core/numeric/singleton_method_added_spec.rb41
-rw-r--r--spec/rubyspec/core/numeric/step_spec.rb151
-rw-r--r--spec/rubyspec/core/numeric/to_c_spec.rb45
-rw-r--r--spec/rubyspec/core/numeric/to_int_spec.rb10
-rw-r--r--spec/rubyspec/core/numeric/truncate_spec.rb14
-rw-r--r--spec/rubyspec/core/numeric/uminus_spec.rb31
-rw-r--r--spec/rubyspec/core/numeric/uplus_spec.rb9
-rw-r--r--spec/rubyspec/core/numeric/zero_spec.rb18
-rw-r--r--spec/rubyspec/core/objectspace/_id2ref_spec.rb25
-rw-r--r--spec/rubyspec/core/objectspace/add_finalizer_spec.rb5
-rw-r--r--spec/rubyspec/core/objectspace/call_finalizer_spec.rb5
-rw-r--r--spec/rubyspec/core/objectspace/count_objects_spec.rb5
-rw-r--r--spec/rubyspec/core/objectspace/define_finalizer_spec.rb101
-rw-r--r--spec/rubyspec/core/objectspace/each_object_spec.rb225
-rw-r--r--spec/rubyspec/core/objectspace/finalizers_spec.rb5
-rw-r--r--spec/rubyspec/core/objectspace/fixtures/classes.rb64
-rw-r--r--spec/rubyspec/core/objectspace/garbage_collect_spec.rb22
-rw-r--r--spec/rubyspec/core/objectspace/remove_finalizer_spec.rb5
-rw-r--r--spec/rubyspec/core/objectspace/undefine_finalizer_spec.rb5
-rw-r--r--spec/rubyspec/core/proc/allocate_spec.rb9
-rw-r--r--spec/rubyspec/core/proc/arity_spec.rb640
-rw-r--r--spec/rubyspec/core/proc/binding_spec.rb21
-rw-r--r--spec/rubyspec/core/proc/block_pass_spec.rb41
-rw-r--r--spec/rubyspec/core/proc/call_spec.rb16
-rw-r--r--spec/rubyspec/core/proc/case_compare_spec.rb16
-rw-r--r--spec/rubyspec/core/proc/clone_spec.rb6
-rw-r--r--spec/rubyspec/core/proc/curry_spec.rb180
-rw-r--r--spec/rubyspec/core/proc/dup_spec.rb6
-rw-r--r--spec/rubyspec/core/proc/element_reference_spec.rb16
-rw-r--r--spec/rubyspec/core/proc/eql_spec.rb6
-rw-r--r--spec/rubyspec/core/proc/equal_value_spec.rb6
-rw-r--r--spec/rubyspec/core/proc/fixtures/common.rb51
-rw-r--r--spec/rubyspec/core/proc/fixtures/source_location.rb55
-rw-r--r--spec/rubyspec/core/proc/hash_spec.rb17
-rw-r--r--spec/rubyspec/core/proc/inspect_spec.rb6
-rw-r--r--spec/rubyspec/core/proc/lambda_spec.rb60
-rw-r--r--spec/rubyspec/core/proc/new_spec.rb190
-rw-r--r--spec/rubyspec/core/proc/parameters_spec.rb95
-rw-r--r--spec/rubyspec/core/proc/shared/call.rb96
-rw-r--r--spec/rubyspec/core/proc/shared/call_arguments.rb7
-rw-r--r--spec/rubyspec/core/proc/shared/dup.rb10
-rw-r--r--spec/rubyspec/core/proc/shared/equal.rb100
-rw-r--r--spec/rubyspec/core/proc/shared/to_s.rb27
-rw-r--r--spec/rubyspec/core/proc/source_location_spec.rb72
-rw-r--r--spec/rubyspec/core/proc/to_proc_spec.rb9
-rw-r--r--spec/rubyspec/core/proc/to_s_spec.rb6
-rw-r--r--spec/rubyspec/core/proc/yield_spec.rb16
-rw-r--r--spec/rubyspec/core/process/abort_spec.rb6
-rw-r--r--spec/rubyspec/core/process/constants_spec.rb63
-rw-r--r--spec/rubyspec/core/process/daemon_spec.rb123
-rw-r--r--spec/rubyspec/core/process/detach_spec.rb46
-rw-r--r--spec/rubyspec/core/process/egid_spec.rb19
-rw-r--r--spec/rubyspec/core/process/euid_spec.rb59
-rw-r--r--spec/rubyspec/core/process/exec_spec.rb216
-rw-r--r--spec/rubyspec/core/process/exit_spec.rb10
-rw-r--r--spec/rubyspec/core/process/fixtures/common.rb79
-rw-r--r--spec/rubyspec/core/process/fixtures/daemon.rb111
-rw-r--r--spec/rubyspec/core/process/fixtures/env.rb1
-rw-r--r--spec/rubyspec/core/process/fixtures/kill.rb49
-rw-r--r--spec/rubyspec/core/process/fixtures/map_fd.rb8
-rw-r--r--spec/rubyspec/core/process/fixtures/print.rb1
-rw-r--r--spec/rubyspec/core/process/fork_spec.rb6
-rw-r--r--spec/rubyspec/core/process/getpgid_spec.rb17
-rw-r--r--spec/rubyspec/core/process/getpgrp_spec.rb7
-rw-r--r--spec/rubyspec/core/process/getpriority_spec.rb23
-rw-r--r--spec/rubyspec/core/process/getrlimit_spec.rb91
-rw-r--r--spec/rubyspec/core/process/gid/change_privilege_spec.rb5
-rw-r--r--spec/rubyspec/core/process/gid/eid_spec.rb9
-rw-r--r--spec/rubyspec/core/process/gid/grant_privilege_spec.rb5
-rw-r--r--spec/rubyspec/core/process/gid/re_exchange_spec.rb5
-rw-r--r--spec/rubyspec/core/process/gid/re_exchangeable_spec.rb5
-rw-r--r--spec/rubyspec/core/process/gid/rid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/gid/sid_available_spec.rb5
-rw-r--r--spec/rubyspec/core/process/gid/switch_spec.rb5
-rw-r--r--spec/rubyspec/core/process/gid_spec.rb22
-rw-r--r--spec/rubyspec/core/process/groups_spec.rb54
-rw-r--r--spec/rubyspec/core/process/initgroups_spec.rb20
-rw-r--r--spec/rubyspec/core/process/kill_spec.rb152
-rw-r--r--spec/rubyspec/core/process/maxgroups_spec.rb19
-rw-r--r--spec/rubyspec/core/process/pid_spec.rb9
-rw-r--r--spec/rubyspec/core/process/ppid_spec.rb23
-rw-r--r--spec/rubyspec/core/process/set_proctitle_spec.rb23
-rw-r--r--spec/rubyspec/core/process/setpgid_spec.rb28
-rw-r--r--spec/rubyspec/core/process/setpgrp_spec.rb37
-rw-r--r--spec/rubyspec/core/process/setpriority_spec.rb60
-rw-r--r--spec/rubyspec/core/process/setrlimit_spec.rb232
-rw-r--r--spec/rubyspec/core/process/setsid_spec.rb37
-rw-r--r--spec/rubyspec/core/process/spawn_spec.rb620
-rw-r--r--spec/rubyspec/core/process/status/bit_and_spec.rb5
-rw-r--r--spec/rubyspec/core/process/status/coredump_spec.rb5
-rw-r--r--spec/rubyspec/core/process/status/equal_value_spec.rb5
-rw-r--r--spec/rubyspec/core/process/status/exited_spec.rb37
-rw-r--r--spec/rubyspec/core/process/status/exitstatus_spec.rb13
-rw-r--r--spec/rubyspec/core/process/status/inspect_spec.rb5
-rw-r--r--spec/rubyspec/core/process/status/pid_spec.rb13
-rw-r--r--spec/rubyspec/core/process/status/right_shift_spec.rb5
-rw-r--r--spec/rubyspec/core/process/status/signaled_spec.rb35
-rw-r--r--spec/rubyspec/core/process/status/stopped_spec.rb5
-rw-r--r--spec/rubyspec/core/process/status/stopsig_spec.rb5
-rw-r--r--spec/rubyspec/core/process/status/success_spec.rb51
-rw-r--r--spec/rubyspec/core/process/status/termsig_spec.rb39
-rw-r--r--spec/rubyspec/core/process/status/to_i_spec.rb5
-rw-r--r--spec/rubyspec/core/process/status/to_int_spec.rb5
-rw-r--r--spec/rubyspec/core/process/status/to_s_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/getegid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/geteuid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/getgid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/getuid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/issetugid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/setegid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/seteuid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/setgid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/setregid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/setresgid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/setresuid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/setreuid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/setrgid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/setruid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/sys/setuid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/times_spec.rb27
-rw-r--r--spec/rubyspec/core/process/uid/change_privilege_spec.rb5
-rw-r--r--spec/rubyspec/core/process/uid/eid_spec.rb9
-rw-r--r--spec/rubyspec/core/process/uid/grant_privilege_spec.rb5
-rw-r--r--spec/rubyspec/core/process/uid/re_exchange_spec.rb5
-rw-r--r--spec/rubyspec/core/process/uid/re_exchangeable_spec.rb5
-rw-r--r--spec/rubyspec/core/process/uid/rid_spec.rb5
-rw-r--r--spec/rubyspec/core/process/uid/sid_available_spec.rb5
-rw-r--r--spec/rubyspec/core/process/uid/switch_spec.rb5
-rw-r--r--spec/rubyspec/core/process/uid_spec.rb84
-rw-r--r--spec/rubyspec/core/process/wait2_spec.rb29
-rw-r--r--spec/rubyspec/core/process/wait_spec.rb87
-rw-r--r--spec/rubyspec/core/process/waitall_spec.rb48
-rw-r--r--spec/rubyspec/core/process/waitpid2_spec.rb5
-rw-r--r--spec/rubyspec/core/process/waitpid_spec.rb15
-rw-r--r--spec/rubyspec/core/random/bytes_spec.rb39
-rw-r--r--spec/rubyspec/core/random/equal_value_spec.rb37
-rw-r--r--spec/rubyspec/core/random/new_seed_spec.rb24
-rw-r--r--spec/rubyspec/core/random/new_spec.rb37
-rw-r--r--spec/rubyspec/core/random/rand_spec.rb216
-rw-r--r--spec/rubyspec/core/random/raw_seed_spec.rb9
-rw-r--r--spec/rubyspec/core/random/seed_spec.rb29
-rw-r--r--spec/rubyspec/core/random/shared/urandom.rb23
-rw-r--r--spec/rubyspec/core/random/srand_spec.rb39
-rw-r--r--spec/rubyspec/core/random/urandom_spec.rb9
-rw-r--r--spec/rubyspec/core/range/begin_spec.rb6
-rw-r--r--spec/rubyspec/core/range/bsearch_spec.rb137
-rw-r--r--spec/rubyspec/core/range/case_compare_spec.rb11
-rw-r--r--spec/rubyspec/core/range/cover_spec.rb9
-rw-r--r--spec/rubyspec/core/range/dup_spec.rb15
-rw-r--r--spec/rubyspec/core/range/each_spec.rb66
-rw-r--r--spec/rubyspec/core/range/end_spec.rb6
-rw-r--r--spec/rubyspec/core/range/eql_spec.rb10
-rw-r--r--spec/rubyspec/core/range/equal_value_spec.rb10
-rw-r--r--spec/rubyspec/core/range/exclude_end_spec.rb19
-rw-r--r--spec/rubyspec/core/range/first_spec.rb49
-rw-r--r--spec/rubyspec/core/range/fixtures/classes.rb65
-rw-r--r--spec/rubyspec/core/range/hash_spec.rb24
-rw-r--r--spec/rubyspec/core/range/include_spec.rb10
-rw-r--r--spec/rubyspec/core/range/initialize_spec.rb41
-rw-r--r--spec/rubyspec/core/range/inspect_spec.rb26
-rw-r--r--spec/rubyspec/core/range/last_spec.rb49
-rw-r--r--spec/rubyspec/core/range/max_spec.rb82
-rw-r--r--spec/rubyspec/core/range/member_spec.rb10
-rw-r--r--spec/rubyspec/core/range/min_spec.rb75
-rw-r--r--spec/rubyspec/core/range/new_spec.rb34
-rw-r--r--spec/rubyspec/core/range/range_spec.rb7
-rw-r--r--spec/rubyspec/core/range/shared/begin.rb10
-rw-r--r--spec/rubyspec/core/range/shared/cover.rb93
-rw-r--r--spec/rubyspec/core/range/shared/cover_and_include.rb66
-rw-r--r--spec/rubyspec/core/range/shared/end.rb10
-rw-r--r--spec/rubyspec/core/range/shared/equal_value.rb45
-rw-r--r--spec/rubyspec/core/range/shared/include.rb91
-rw-r--r--spec/rubyspec/core/range/size_spec.rb31
-rw-r--r--spec/rubyspec/core/range/step_spec.rb347
-rw-r--r--spec/rubyspec/core/range/to_a_spec.rb22
-rw-r--r--spec/rubyspec/core/range/to_s_spec.rb25
-rw-r--r--spec/rubyspec/core/rational/abs_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/ceil_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/coerce_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/comparison_spec.rb21
-rw-r--r--spec/rubyspec/core/rational/denominator_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/div_spec.rb17
-rw-r--r--spec/rubyspec/core/rational/divide_spec.rb17
-rw-r--r--spec/rubyspec/core/rational/divmod_spec.rb13
-rw-r--r--spec/rubyspec/core/rational/equal_value_spec.rb17
-rw-r--r--spec/rubyspec/core/rational/exponent_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/fdiv_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/floor_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/hash_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/inspect_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/integer_spec.rb9
-rw-r--r--spec/rubyspec/core/rational/magnitude_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/marshal_dump_spec.rb11
-rw-r--r--spec/rubyspec/core/rational/minus_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/modulo_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/multiply_spec.rb17
-rw-r--r--spec/rubyspec/core/rational/numerator_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/plus_spec.rb16
-rw-r--r--spec/rubyspec/core/rational/quo_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/rational_spec.rb7
-rw-r--r--spec/rubyspec/core/rational/rationalize_spec.rb36
-rw-r--r--spec/rubyspec/core/rational/remainder_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/round_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/to_f_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/to_i_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/to_r_spec.rb10
-rw-r--r--spec/rubyspec/core/rational/to_s_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/truncate_spec.rb5
-rw-r--r--spec/rubyspec/core/rational/zero_spec.rb13
-rw-r--r--spec/rubyspec/core/regexp/case_compare_spec.rb25
-rw-r--r--spec/rubyspec/core/regexp/casefold_spec.rb8
-rw-r--r--spec/rubyspec/core/regexp/compile_spec.rb18
-rw-r--r--spec/rubyspec/core/regexp/encoding_spec.rb58
-rw-r--r--spec/rubyspec/core/regexp/eql_spec.rb6
-rw-r--r--spec/rubyspec/core/regexp/equal_value_spec.rb6
-rw-r--r--spec/rubyspec/core/regexp/escape_spec.rb6
-rw-r--r--spec/rubyspec/core/regexp/fixed_encoding_spec.rb36
-rw-r--r--spec/rubyspec/core/regexp/hash_spec.rb20
-rw-r--r--spec/rubyspec/core/regexp/initialize_spec.rb15
-rw-r--r--spec/rubyspec/core/regexp/inspect_spec.rb44
-rw-r--r--spec/rubyspec/core/regexp/last_match_spec.rb14
-rw-r--r--spec/rubyspec/core/regexp/match_spec.rb148
-rw-r--r--spec/rubyspec/core/regexp/named_captures_spec.rb35
-rw-r--r--spec/rubyspec/core/regexp/names_spec.rb29
-rw-r--r--spec/rubyspec/core/regexp/new_spec.rb30
-rw-r--r--spec/rubyspec/core/regexp/options_spec.rb54
-rw-r--r--spec/rubyspec/core/regexp/quote_spec.rb6
-rw-r--r--spec/rubyspec/core/regexp/shared/equal_value.rb31
-rw-r--r--spec/rubyspec/core/regexp/shared/new_ascii.rb464
-rw-r--r--spec/rubyspec/core/regexp/shared/new_ascii_8bit.rb553
-rw-r--r--spec/rubyspec/core/regexp/shared/quote.rb31
-rw-r--r--spec/rubyspec/core/regexp/source_spec.rb29
-rw-r--r--spec/rubyspec/core/regexp/to_s_spec.rb62
-rw-r--r--spec/rubyspec/core/regexp/try_convert_spec.rb21
-rw-r--r--spec/rubyspec/core/regexp/union_spec.rb149
-rw-r--r--spec/rubyspec/core/signal/list_spec.rb64
-rw-r--r--spec/rubyspec/core/signal/signame_spec.rb23
-rw-r--r--spec/rubyspec/core/signal/trap_spec.rb135
-rw-r--r--spec/rubyspec/core/string/allocate_spec.rb19
-rw-r--r--spec/rubyspec/core/string/append_spec.rb8
-rw-r--r--spec/rubyspec/core/string/ascii_only_spec.rb85
-rw-r--r--spec/rubyspec/core/string/b_spec.rb24
-rw-r--r--spec/rubyspec/core/string/bytes_spec.rb57
-rw-r--r--spec/rubyspec/core/string/bytesize_spec.rb37
-rw-r--r--spec/rubyspec/core/string/byteslice_spec.rb29
-rw-r--r--spec/rubyspec/core/string/capitalize_spec.rb56
-rw-r--r--spec/rubyspec/core/string/case_compare_spec.rb8
-rw-r--r--spec/rubyspec/core/string/casecmp_spec.rb111
-rw-r--r--spec/rubyspec/core/string/center_spec.rb133
-rw-r--r--spec/rubyspec/core/string/chars_spec.rb11
-rw-r--r--spec/rubyspec/core/string/chomp_spec.rb387
-rw-r--r--spec/rubyspec/core/string/chop_spec.rb128
-rw-r--r--spec/rubyspec/core/string/chr_spec.rb44
-rw-r--r--spec/rubyspec/core/string/clear_spec.rb39
-rw-r--r--spec/rubyspec/core/string/clone_spec.rb58
-rw-r--r--spec/rubyspec/core/string/codepoints_spec.rb20
-rw-r--r--spec/rubyspec/core/string/comparison_spec.rb108
-rw-r--r--spec/rubyspec/core/string/concat_spec.rb28
-rw-r--r--spec/rubyspec/core/string/count_spec.rb105
-rw-r--r--spec/rubyspec/core/string/crypt_spec.rb75
-rw-r--r--spec/rubyspec/core/string/delete_spec.rb119
-rw-r--r--spec/rubyspec/core/string/downcase_spec.rb59
-rw-r--r--spec/rubyspec/core/string/dump_spec.rb424
-rw-r--r--spec/rubyspec/core/string/dup_spec.rb52
-rw-r--r--spec/rubyspec/core/string/each_byte_spec.rb61
-rw-r--r--spec/rubyspec/core/string/each_char_spec.rb7
-rw-r--r--spec/rubyspec/core/string/each_codepoint_spec.rb10
-rw-r--r--spec/rubyspec/core/string/each_line_spec.rb9
-rw-r--r--spec/rubyspec/core/string/element_reference_spec.rb35
-rw-r--r--spec/rubyspec/core/string/element_set_spec.rb612
-rw-r--r--spec/rubyspec/core/string/empty_spec.rb12
-rw-r--r--spec/rubyspec/core/string/encode_spec.rb159
-rw-r--r--spec/rubyspec/core/string/encoding_spec.rb189
-rw-r--r--spec/rubyspec/core/string/end_with_spec.rb50
-rw-r--r--spec/rubyspec/core/string/eql_spec.rb21
-rw-r--r--spec/rubyspec/core/string/equal_value_spec.rb8
-rw-r--r--spec/rubyspec/core/string/fixtures/classes.rb49
-rw-r--r--spec/rubyspec/core/string/fixtures/freeze_magic_comment.rb3
-rw-r--r--spec/rubyspec/core/string/fixtures/iso-8859-9-encoding.rb9
-rw-r--r--spec/rubyspec/core/string/fixtures/utf-8-encoding.rb7
-rw-r--r--spec/rubyspec/core/string/force_encoding_spec.rb53
-rw-r--r--spec/rubyspec/core/string/freeze_spec.rb18
-rw-r--r--spec/rubyspec/core/string/getbyte_spec.rb69
-rw-r--r--spec/rubyspec/core/string/gsub_spec.rb696
-rw-r--r--spec/rubyspec/core/string/hash_spec.rb9
-rw-r--r--spec/rubyspec/core/string/hex_spec.rb49
-rw-r--r--spec/rubyspec/core/string/include_spec.rb28
-rw-r--r--spec/rubyspec/core/string/index_spec.rb315
-rw-r--r--spec/rubyspec/core/string/initialize_spec.rb26
-rw-r--r--spec/rubyspec/core/string/insert_spec.rb84
-rw-r--r--spec/rubyspec/core/string/inspect_spec.rb492
-rw-r--r--spec/rubyspec/core/string/intern_spec.rb7
-rw-r--r--spec/rubyspec/core/string/length_spec.rb7
-rw-r--r--spec/rubyspec/core/string/lines_spec.rb13
-rw-r--r--spec/rubyspec/core/string/ljust_spec.rb116
-rw-r--r--spec/rubyspec/core/string/lstrip_spec.rb50
-rw-r--r--spec/rubyspec/core/string/match_spec.rb175
-rw-r--r--spec/rubyspec/core/string/modulo_spec.rb780
-rw-r--r--spec/rubyspec/core/string/multiply_spec.rb7
-rw-r--r--spec/rubyspec/core/string/new_spec.rb58
-rw-r--r--spec/rubyspec/core/string/next_spec.rb11
-rw-r--r--spec/rubyspec/core/string/oct_spec.rb88
-rw-r--r--spec/rubyspec/core/string/ord_spec.rb30
-rw-r--r--spec/rubyspec/core/string/partition_spec.rb38
-rw-r--r--spec/rubyspec/core/string/plus_spec.rb47
-rw-r--r--spec/rubyspec/core/string/prepend_spec.rb64
-rw-r--r--spec/rubyspec/core/string/replace_spec.rb7
-rw-r--r--spec/rubyspec/core/string/reverse_spec.rb52
-rw-r--r--spec/rubyspec/core/string/rindex_spec.rb368
-rw-r--r--spec/rubyspec/core/string/rjust_spec.rb116
-rw-r--r--spec/rubyspec/core/string/rpartition_spec.rb33
-rw-r--r--spec/rubyspec/core/string/rstrip_spec.rb52
-rw-r--r--spec/rubyspec/core/string/scan_spec.rb192
-rw-r--r--spec/rubyspec/core/string/scrub_spec.rb101
-rw-r--r--spec/rubyspec/core/string/setbyte_spec.rb105
-rw-r--r--spec/rubyspec/core/string/shared/chars.rb82
-rw-r--r--spec/rubyspec/core/string/shared/codepoints.rb56
-rw-r--r--spec/rubyspec/core/string/shared/concat.rb160
-rw-r--r--spec/rubyspec/core/string/shared/each_char_without_block.rb26
-rw-r--r--spec/rubyspec/core/string/shared/each_codepoint_without_block.rb33
-rw-r--r--spec/rubyspec/core/string/shared/each_line.rb136
-rw-r--r--spec/rubyspec/core/string/shared/each_line_without_block.rb17
-rw-r--r--spec/rubyspec/core/string/shared/encode.rb247
-rw-r--r--spec/rubyspec/core/string/shared/eql.rb34
-rw-r--r--spec/rubyspec/core/string/shared/equal_value.rb29
-rw-r--r--spec/rubyspec/core/string/shared/length.rb28
-rw-r--r--spec/rubyspec/core/string/shared/replace.rb75
-rw-r--r--spec/rubyspec/core/string/shared/slice.rb533
-rw-r--r--spec/rubyspec/core/string/shared/succ.rb88
-rw-r--r--spec/rubyspec/core/string/shared/to_a.rb9
-rw-r--r--spec/rubyspec/core/string/shared/to_s.rb18
-rw-r--r--spec/rubyspec/core/string/shared/to_sym.rb24
-rw-r--r--spec/rubyspec/core/string/size_spec.rb7
-rw-r--r--spec/rubyspec/core/string/slice_spec.rb476
-rw-r--r--spec/rubyspec/core/string/split_spec.rb401
-rw-r--r--spec/rubyspec/core/string/squeeze_spec.rb113
-rw-r--r--spec/rubyspec/core/string/start_with_spec.rb45
-rw-r--r--spec/rubyspec/core/string/string_spec.rb7
-rw-r--r--spec/rubyspec/core/string/strip_spec.rb60
-rw-r--r--spec/rubyspec/core/string/sub_spec.rb571
-rw-r--r--spec/rubyspec/core/string/succ_spec.rb11
-rw-r--r--spec/rubyspec/core/string/sum_spec.rb22
-rw-r--r--spec/rubyspec/core/string/swapcase_spec.rb52
-rw-r--r--spec/rubyspec/core/string/to_c_spec.rb99
-rw-r--r--spec/rubyspec/core/string/to_f_spec.rb69
-rw-r--r--spec/rubyspec/core/string/to_i_spec.rb337
-rw-r--r--spec/rubyspec/core/string/to_r_spec.rb58
-rw-r--r--spec/rubyspec/core/string/to_s_spec.rb7
-rw-r--r--spec/rubyspec/core/string/to_str_spec.rb7
-rw-r--r--spec/rubyspec/core/string/to_sym_spec.rb7
-rw-r--r--spec/rubyspec/core/string/tr_s_spec.rb136
-rw-r--r--spec/rubyspec/core/string/tr_spec.rb131
-rw-r--r--spec/rubyspec/core/string/try_convert_spec.rb50
-rw-r--r--spec/rubyspec/core/string/uminus_spec.rb21
-rw-r--r--spec/rubyspec/core/string/unicode_normalize_spec.rb116
-rw-r--r--spec/rubyspec/core/string/unicode_normalized_spec.rb74
-rw-r--r--spec/rubyspec/core/string/unpack/a_spec.rb63
-rw-r--r--spec/rubyspec/core/string/unpack/at_spec.rb29
-rw-r--r--spec/rubyspec/core/string/unpack/b_spec.rb190
-rw-r--r--spec/rubyspec/core/string/unpack/c_spec.rb63
-rw-r--r--spec/rubyspec/core/string/unpack/comment_spec.rb25
-rw-r--r--spec/rubyspec/core/string/unpack/d_spec.rb28
-rw-r--r--spec/rubyspec/core/string/unpack/e_spec.rb14
-rw-r--r--spec/rubyspec/core/string/unpack/f_spec.rb28
-rw-r--r--spec/rubyspec/core/string/unpack/g_spec.rb14
-rw-r--r--spec/rubyspec/core/string/unpack/h_spec.rb124
-rw-r--r--spec/rubyspec/core/string/unpack/i_spec.rb152
-rw-r--r--spec/rubyspec/core/string/unpack/j_spec.rb277
-rw-r--r--spec/rubyspec/core/string/unpack/l_spec.rb376
-rw-r--r--spec/rubyspec/core/string/unpack/m_spec.rb170
-rw-r--r--spec/rubyspec/core/string/unpack/n_spec.rb18
-rw-r--r--spec/rubyspec/core/string/unpack/p_spec.rb21
-rw-r--r--spec/rubyspec/core/string/unpack/percent_spec.rb7
-rw-r--r--spec/rubyspec/core/string/unpack/q_spec.rb64
-rw-r--r--spec/rubyspec/core/string/unpack/s_spec.rb152
-rw-r--r--spec/rubyspec/core/string/unpack/shared/basic.rb29
-rw-r--r--spec/rubyspec/core/string/unpack/shared/float.rb271
-rw-r--r--spec/rubyspec/core/string/unpack/shared/integer.rb339
-rw-r--r--spec/rubyspec/core/string/unpack/shared/string.rb51
-rw-r--r--spec/rubyspec/core/string/unpack/shared/unicode.rb60
-rw-r--r--spec/rubyspec/core/string/unpack/u_spec.rb94
-rw-r--r--spec/rubyspec/core/string/unpack/v_spec.rb18
-rw-r--r--spec/rubyspec/core/string/unpack/w_spec.rb25
-rw-r--r--spec/rubyspec/core/string/unpack/x_spec.rb62
-rw-r--r--spec/rubyspec/core/string/unpack/z_spec.rb21
-rw-r--r--spec/rubyspec/core/string/upcase_spec.rb53
-rw-r--r--spec/rubyspec/core/string/uplus_spec.rb24
-rw-r--r--spec/rubyspec/core/string/upto_spec.rb98
-rw-r--r--spec/rubyspec/core/string/valid_encoding_spec.rb129
-rw-r--r--spec/rubyspec/core/struct/dig_spec.rb44
-rw-r--r--spec/rubyspec/core/struct/dup_spec.rb20
-rw-r--r--spec/rubyspec/core/struct/each_pair_spec.rb33
-rw-r--r--spec/rubyspec/core/struct/each_spec.rb27
-rw-r--r--spec/rubyspec/core/struct/element_reference_spec.rb52
-rw-r--r--spec/rubyspec/core/struct/element_set_spec.rb29
-rw-r--r--spec/rubyspec/core/struct/eql_spec.rb13
-rw-r--r--spec/rubyspec/core/struct/equal_value_spec.rb7
-rw-r--r--spec/rubyspec/core/struct/fixtures/classes.rb26
-rw-r--r--spec/rubyspec/core/struct/hash_spec.rb46
-rw-r--r--spec/rubyspec/core/struct/initialize_spec.rb43
-rw-r--r--spec/rubyspec/core/struct/inspect_spec.rb18
-rw-r--r--spec/rubyspec/core/struct/instance_variables_spec.rb16
-rw-r--r--spec/rubyspec/core/struct/length_spec.rb12
-rw-r--r--spec/rubyspec/core/struct/members_spec.rb13
-rw-r--r--spec/rubyspec/core/struct/new_spec.rb123
-rw-r--r--spec/rubyspec/core/struct/select_spec.rb30
-rw-r--r--spec/rubyspec/core/struct/shared/accessor.rb7
-rw-r--r--spec/rubyspec/core/struct/shared/equal_value.rb30
-rw-r--r--spec/rubyspec/core/struct/shared/inspect.rb5
-rw-r--r--spec/rubyspec/core/struct/size_spec.rb11
-rw-r--r--spec/rubyspec/core/struct/struct_spec.rb43
-rw-r--r--spec/rubyspec/core/struct/tms/cstime_spec.rb9
-rw-r--r--spec/rubyspec/core/struct/tms/cutime_spec.rb9
-rw-r--r--spec/rubyspec/core/struct/tms/element_reference_spec.rb5
-rw-r--r--spec/rubyspec/core/struct/tms/members_spec.rb5
-rw-r--r--spec/rubyspec/core/struct/tms/new_spec.rb5
-rw-r--r--spec/rubyspec/core/struct/tms/stime_spec.rb9
-rw-r--r--spec/rubyspec/core/struct/tms/utime_spec.rb9
-rw-r--r--spec/rubyspec/core/struct/to_a_spec.rb12
-rw-r--r--spec/rubyspec/core/struct/to_h_spec.rb15
-rw-r--r--spec/rubyspec/core/struct/to_s_spec.rb12
-rw-r--r--spec/rubyspec/core/struct/values_at_spec.rb16
-rw-r--r--spec/rubyspec/core/struct/values_spec.rb11
-rw-r--r--spec/rubyspec/core/symbol/all_symbols_spec.rb14
-rw-r--r--spec/rubyspec/core/symbol/capitalize_spec.rb49
-rw-r--r--spec/rubyspec/core/symbol/case_compare_spec.rb11
-rw-r--r--spec/rubyspec/core/symbol/casecmp_spec.rb74
-rw-r--r--spec/rubyspec/core/symbol/comparison_spec.rb51
-rw-r--r--spec/rubyspec/core/symbol/downcase_spec.rb26
-rw-r--r--spec/rubyspec/core/symbol/element_reference_spec.rb6
-rw-r--r--spec/rubyspec/core/symbol/empty_spec.rb11
-rw-r--r--spec/rubyspec/core/symbol/encoding_spec.rb23
-rw-r--r--spec/rubyspec/core/symbol/equal_value_spec.rb14
-rw-r--r--spec/rubyspec/core/symbol/fixtures/classes.rb3
-rw-r--r--spec/rubyspec/core/symbol/id2name_spec.rb6
-rw-r--r--spec/rubyspec/core/symbol/inspect_spec.rb105
-rw-r--r--spec/rubyspec/core/symbol/intern_spec.rb11
-rw-r--r--spec/rubyspec/core/symbol/length_spec.rb6
-rw-r--r--spec/rubyspec/core/symbol/match_spec.rb70
-rw-r--r--spec/rubyspec/core/symbol/next_spec.rb6
-rw-r--r--spec/rubyspec/core/symbol/shared/id2name.rb9
-rw-r--r--spec/rubyspec/core/symbol/shared/length.rb23
-rw-r--r--spec/rubyspec/core/symbol/shared/slice.rb278
-rw-r--r--spec/rubyspec/core/symbol/shared/succ.rb18
-rw-r--r--spec/rubyspec/core/symbol/size_spec.rb6
-rw-r--r--spec/rubyspec/core/symbol/slice_spec.rb6
-rw-r--r--spec/rubyspec/core/symbol/succ_spec.rb6
-rw-r--r--spec/rubyspec/core/symbol/swapcase_spec.rb34
-rw-r--r--spec/rubyspec/core/symbol/symbol_spec.rb7
-rw-r--r--spec/rubyspec/core/symbol/to_proc_spec.rb41
-rw-r--r--spec/rubyspec/core/symbol/to_s_spec.rb6
-rw-r--r--spec/rubyspec/core/symbol/to_sym_spec.rb9
-rw-r--r--spec/rubyspec/core/symbol/upcase_spec.rb22
-rw-r--r--spec/rubyspec/core/systemexit/initialize_spec.rb27
-rw-r--r--spec/rubyspec/core/systemexit/success_spec.rb13
-rw-r--r--spec/rubyspec/core/thread/abort_on_exception_spec.rb106
-rw-r--r--spec/rubyspec/core/thread/add_trace_func_spec.rb5
-rw-r--r--spec/rubyspec/core/thread/alive_spec.rb58
-rw-r--r--spec/rubyspec/core/thread/allocate_spec.rb9
-rw-r--r--spec/rubyspec/core/thread/backtrace/location/absolute_path_spec.rb12
-rw-r--r--spec/rubyspec/core/thread/backtrace/location/base_label_spec.rb12
-rw-r--r--spec/rubyspec/core/thread/backtrace/location/fixtures/classes.rb17
-rw-r--r--spec/rubyspec/core/thread/backtrace/location/fixtures/main.rb5
-rw-r--r--spec/rubyspec/core/thread/backtrace/location/inspect_spec.rb13
-rw-r--r--spec/rubyspec/core/thread/backtrace/location/label_spec.rb20
-rw-r--r--spec/rubyspec/core/thread/backtrace/location/lineno_spec.rb13
-rw-r--r--spec/rubyspec/core/thread/backtrace/location/path_spec.rb91
-rw-r--r--spec/rubyspec/core/thread/backtrace/location/to_s_spec.rb13
-rw-r--r--spec/rubyspec/core/thread/backtrace_spec.rb27
-rw-r--r--spec/rubyspec/core/thread/current_spec.rb15
-rw-r--r--spec/rubyspec/core/thread/element_reference_spec.rb44
-rw-r--r--spec/rubyspec/core/thread/element_set_spec.rb52
-rw-r--r--spec/rubyspec/core/thread/exclusive_spec.rb18
-rw-r--r--spec/rubyspec/core/thread/exit_spec.rb15
-rw-r--r--spec/rubyspec/core/thread/fixtures/classes.rb288
-rw-r--r--spec/rubyspec/core/thread/fork_spec.rb9
-rw-r--r--spec/rubyspec/core/thread/group_spec.rb5
-rw-r--r--spec/rubyspec/core/thread/initialize_spec.rb27
-rw-r--r--spec/rubyspec/core/thread/inspect_spec.rb42
-rw-r--r--spec/rubyspec/core/thread/join_spec.rb62
-rw-r--r--spec/rubyspec/core/thread/key_spec.rb53
-rw-r--r--spec/rubyspec/core/thread/keys_spec.rb44
-rw-r--r--spec/rubyspec/core/thread/kill_spec.rb21
-rw-r--r--spec/rubyspec/core/thread/list_spec.rb42
-rw-r--r--spec/rubyspec/core/thread/main_spec.rb10
-rw-r--r--spec/rubyspec/core/thread/name_spec.rb56
-rw-r--r--spec/rubyspec/core/thread/new_spec.rb56
-rw-r--r--spec/rubyspec/core/thread/pass_spec.rb8
-rw-r--r--spec/rubyspec/core/thread/priority_spec.rb68
-rw-r--r--spec/rubyspec/core/thread/raise_spec.rb175
-rw-r--r--spec/rubyspec/core/thread/run_spec.rb9
-rw-r--r--spec/rubyspec/core/thread/set_trace_func_spec.rb5
-rw-r--r--spec/rubyspec/core/thread/shared/exit.rb176
-rw-r--r--spec/rubyspec/core/thread/shared/start.rb41
-rw-r--r--spec/rubyspec/core/thread/shared/wakeup.rb61
-rw-r--r--spec/rubyspec/core/thread/start_spec.rb9
-rw-r--r--spec/rubyspec/core/thread/status_spec.rb42
-rw-r--r--spec/rubyspec/core/thread/stop_spec.rb56
-rw-r--r--spec/rubyspec/core/thread/terminate_spec.rb11
-rw-r--r--spec/rubyspec/core/thread/thread_variable_get_spec.rb25
-rw-r--r--spec/rubyspec/core/thread/thread_variable_set_spec.rb26
-rw-r--r--spec/rubyspec/core/thread/thread_variable_spec.rb21
-rw-r--r--spec/rubyspec/core/thread/thread_variables_spec.rb24
-rw-r--r--spec/rubyspec/core/thread/value_spec.rb18
-rw-r--r--spec/rubyspec/core/thread/wakeup_spec.rb7
-rw-r--r--spec/rubyspec/core/threadgroup/add_spec.rb36
-rw-r--r--spec/rubyspec/core/threadgroup/default_spec.rb11
-rw-r--r--spec/rubyspec/core/threadgroup/enclose_spec.rb25
-rw-r--r--spec/rubyspec/core/threadgroup/enclosed_spec.rb14
-rw-r--r--spec/rubyspec/core/threadgroup/fixtures/classes.rb6
-rw-r--r--spec/rubyspec/core/threadgroup/list_spec.rb24
-rw-r--r--spec/rubyspec/core/time/_dump_spec.rb56
-rw-r--r--spec/rubyspec/core/time/_load_spec.rb54
-rw-r--r--spec/rubyspec/core/time/asctime_spec.rb6
-rw-r--r--spec/rubyspec/core/time/at_spec.rb145
-rw-r--r--spec/rubyspec/core/time/comparison_spec.rb94
-rw-r--r--spec/rubyspec/core/time/ctime_spec.rb6
-rw-r--r--spec/rubyspec/core/time/day_spec.rb6
-rw-r--r--spec/rubyspec/core/time/dst_spec.rb6
-rw-r--r--spec/rubyspec/core/time/dup_spec.rb39
-rw-r--r--spec/rubyspec/core/time/eql_spec.rb29
-rw-r--r--spec/rubyspec/core/time/fixtures/classes.rb12
-rw-r--r--spec/rubyspec/core/time/friday_spec.rb11
-rw-r--r--spec/rubyspec/core/time/getgm_spec.rb6
-rw-r--r--spec/rubyspec/core/time/getlocal_spec.rb98
-rw-r--r--spec/rubyspec/core/time/getutc_spec.rb6
-rw-r--r--spec/rubyspec/core/time/gm_spec.rb10
-rw-r--r--spec/rubyspec/core/time/gmt_offset_spec.rb6
-rw-r--r--spec/rubyspec/core/time/gmt_spec.rb8
-rw-r--r--spec/rubyspec/core/time/gmtime_spec.rb6
-rw-r--r--spec/rubyspec/core/time/gmtoff_spec.rb6
-rw-r--r--spec/rubyspec/core/time/hash_spec.rb11
-rw-r--r--spec/rubyspec/core/time/hour_spec.rb17
-rw-r--r--spec/rubyspec/core/time/inspect_spec.rb6
-rw-r--r--spec/rubyspec/core/time/isdst_spec.rb6
-rw-r--r--spec/rubyspec/core/time/local_spec.rb11
-rw-r--r--spec/rubyspec/core/time/localtime_spec.rb113
-rw-r--r--spec/rubyspec/core/time/mday_spec.rb6
-rw-r--r--spec/rubyspec/core/time/min_spec.rb17
-rw-r--r--spec/rubyspec/core/time/minus_spec.rb103
-rw-r--r--spec/rubyspec/core/time/mktime_spec.rb11
-rw-r--r--spec/rubyspec/core/time/mon_spec.rb6
-rw-r--r--spec/rubyspec/core/time/monday_spec.rb11
-rw-r--r--spec/rubyspec/core/time/month_spec.rb6
-rw-r--r--spec/rubyspec/core/time/new_spec.rb99
-rw-r--r--spec/rubyspec/core/time/now_spec.rb6
-rw-r--r--spec/rubyspec/core/time/nsec_spec.rb27
-rw-r--r--spec/rubyspec/core/time/plus_spec.rb100
-rw-r--r--spec/rubyspec/core/time/round_spec.rb33
-rw-r--r--spec/rubyspec/core/time/saturday_spec.rb11
-rw-r--r--spec/rubyspec/core/time/sec_spec.rb7
-rw-r--r--spec/rubyspec/core/time/shared/asctime.rb6
-rw-r--r--spec/rubyspec/core/time/shared/day.rb15
-rw-r--r--spec/rubyspec/core/time/shared/getgm.rb9
-rw-r--r--spec/rubyspec/core/time/shared/gm.rb22
-rw-r--r--spec/rubyspec/core/time/shared/gmt_offset.rb53
-rw-r--r--spec/rubyspec/core/time/shared/gmtime.rb10
-rw-r--r--spec/rubyspec/core/time/shared/inspect.rb23
-rw-r--r--spec/rubyspec/core/time/shared/isdst.rb8
-rw-r--r--spec/rubyspec/core/time/shared/local.rb45
-rw-r--r--spec/rubyspec/core/time/shared/month.rb15
-rw-r--r--spec/rubyspec/core/time/shared/now.rb8
-rw-r--r--spec/rubyspec/core/time/shared/time_params.rb255
-rw-r--r--spec/rubyspec/core/time/shared/to_i.rb9
-rw-r--r--spec/rubyspec/core/time/strftime_spec.rb52
-rw-r--r--spec/rubyspec/core/time/subsec_spec.rb27
-rw-r--r--spec/rubyspec/core/time/succ_spec.rb19
-rw-r--r--spec/rubyspec/core/time/sunday_spec.rb11
-rw-r--r--spec/rubyspec/core/time/thursday_spec.rb11
-rw-r--r--spec/rubyspec/core/time/time_spec.rb7
-rw-r--r--spec/rubyspec/core/time/to_a_spec.rb12
-rw-r--r--spec/rubyspec/core/time/to_f_spec.rb7
-rw-r--r--spec/rubyspec/core/time/to_i_spec.rb6
-rw-r--r--spec/rubyspec/core/time/to_r_spec.rb11
-rw-r--r--spec/rubyspec/core/time/to_s_spec.rb6
-rw-r--r--spec/rubyspec/core/time/tuesday_spec.rb11
-rw-r--r--spec/rubyspec/core/time/tv_nsec_spec.rb5
-rw-r--r--spec/rubyspec/core/time/tv_sec_spec.rb6
-rw-r--r--spec/rubyspec/core/time/tv_usec_spec.rb5
-rw-r--r--spec/rubyspec/core/time/usec_spec.rb39
-rw-r--r--spec/rubyspec/core/time/utc_offset_spec.rb6
-rw-r--r--spec/rubyspec/core/time/utc_spec.rb21
-rw-r--r--spec/rubyspec/core/time/wday_spec.rb9
-rw-r--r--spec/rubyspec/core/time/wednesday_spec.rb11
-rw-r--r--spec/rubyspec/core/time/yday_spec.rb21
-rw-r--r--spec/rubyspec/core/time/year_spec.rb17
-rw-r--r--spec/rubyspec/core/time/zone_spec.rb78
-rw-r--r--spec/rubyspec/core/true/and_spec.rb11
-rw-r--r--spec/rubyspec/core/true/inspect_spec.rb7
-rw-r--r--spec/rubyspec/core/true/or_spec.rb11
-rw-r--r--spec/rubyspec/core/true/to_s_spec.rb7
-rw-r--r--spec/rubyspec/core/true/xor_spec.rb11
-rw-r--r--spec/rubyspec/core/unboundmethod/arity_spec.rb207
-rw-r--r--spec/rubyspec/core/unboundmethod/bind_spec.rb51
-rw-r--r--spec/rubyspec/core/unboundmethod/clone_spec.rb12
-rw-r--r--spec/rubyspec/core/unboundmethod/eql_spec.rb5
-rw-r--r--spec/rubyspec/core/unboundmethod/equal_value_spec.rb101
-rw-r--r--spec/rubyspec/core/unboundmethod/fixtures/classes.rb86
-rw-r--r--spec/rubyspec/core/unboundmethod/hash_spec.rb17
-rw-r--r--spec/rubyspec/core/unboundmethod/inspect_spec.rb7
-rw-r--r--spec/rubyspec/core/unboundmethod/name_spec.rb15
-rw-r--r--spec/rubyspec/core/unboundmethod/owner_spec.rb26
-rw-r--r--spec/rubyspec/core/unboundmethod/parameters_spec.rb5
-rw-r--r--spec/rubyspec/core/unboundmethod/shared/to_s.rb25
-rw-r--r--spec/rubyspec/core/unboundmethod/source_location_spec.rb52
-rw-r--r--spec/rubyspec/core/unboundmethod/super_method_spec.rb28
-rw-r--r--spec/rubyspec/core/unboundmethod/to_s_spec.rb7
2081 files changed, 102420 insertions, 0 deletions
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<!'
+ it_behaves_like :array_pack_32bit_le, 'I!<'
+ end
+
+ describe "with modifier '>'" 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<!'
+ it_behaves_like :array_pack_32bit_le, 'i!<'
+ end
+
+ describe "with modifier '>'" 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<!'
+ it_behaves_like :array_pack_64bit_le, 'J!<'
+ end
+
+ describe "with modifier '>' 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<!'
+ it_behaves_like :array_pack_64bit_le, 'j!<'
+ end
+
+ describe "with modifier '>' 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<!'
+ it_behaves_like :array_pack_32bit_le, 'J!<'
+ end
+
+ describe "with modifier '>' 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<!'
+ it_behaves_like :array_pack_32bit_le, 'j!<'
+ end
+
+ describe "with modifier '>' 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<!'
+ it_behaves_like :array_pack_32bit_le, 'L!<'
+ end
+
+ describe "with modifier '>' 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<!'
+ it_behaves_like :array_pack_64bit_le, 'L!<'
+ end
+
+ describe "with modifier '>' 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<!'
+ it_behaves_like :array_pack_32bit_le, 'L!<'
+ end
+
+ describe "with modifier '>' 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<!'
+ it_behaves_like :array_pack_32bit_le, 'l!<'
+ end
+
+ describe "with modifier '>' 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<!'
+ it_behaves_like :array_pack_64bit_le, 'l!<'
+ end
+
+ describe "with modifier '>' 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<!'
+ it_behaves_like :array_pack_32bit_le, 'l!<'
+ end
+
+ describe "with modifier '>' 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<!'
+ it_behaves_like :array_pack_16bit_le, 'S!<'
+ end
+
+ describe "with modifier '>'" 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<!'
+ it_behaves_like :array_pack_16bit_le, 's!<'
+ end
+
+ describe "with modifier '>'" 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<X.0``\n"],
+ [[":;<=>?@"], "'.CL\\/3X_0```\n"],
+ [["ABCDEFGHIJKLMNOPQRSTUVWXYZ"], ":04)#1$5&1TA)2DM,34Y/4%%24U155E=865H`\n"],
+ [["[\\]^_`"], "&6UQ=7E]@\n"],
+ [["abcdefghijklmnopqrstuvwxyz"], ":86)C9&5F9VAI:FML;6YO<'%R<W1U=G=X>7H`\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 == '#<Class:CoreClassSpecs>'
+ end
+
+ it 'for classes includes class name' do
+ CoreClassSpecs::Record.singleton_class.to_s.should == '#<Class:CoreClassSpecs::Record>'
+ end
+
+ it 'for objects includes class name and object ID' do
+ obj = CoreClassSpecs::Record.new
+ obj.singleton_class.to_s.should =~ /#<Class:#<CoreClassSpecs::Record:0x[0-9a-f]+>>/
+ 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 '*<non-special characters>' 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 '*<non-special characters>'" do
+ Dir.send(@method, '*file').sort.should == %w|nondotfile|.sort
+ end
+
+ it "matches dotfiles with '.*<non-special characters>'" do
+ Dir.send(@method, '.*file').sort.should == %w|.dotfile|.sort
+ end
+
+ it "matches files with any ending with '<non-special characters>*'" do
+ Dir.send(@method, 'file*').sort.should == %w|file_one.ext file_two.ext|.sort
+ end
+
+ it "matches files with any middle with '<non-special characters>*<non-special characters>'" 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 '[<characters>]'" do
+ Dir.send(@method, '[stfu]ubdir_one').sort.should == %w|subdir_one|.sort
+ end
+
+ it "matches any characters in a range with '[<character>-<character>]'" 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 '[^<characters>]'" do
+ Dir.send(@method, '[^wtf]ubdir_one').sort.should == %w|subdir_one|.sort
+ end
+
+ it "matches any characters except those in a range with '[^<character>-<character]'" do
+ Dir.send(@method, '[^0-9]ubdir_one').sort.should == %w|subdir_one|.sort
+ end
+
+ it "matches any one of the strings in a set with '{<string>,<other>,...}'" do
+ Dir.send(@method, 'subdir_{one,two,three}').sort.should == %w|subdir_one subdir_two|.sort
+ end
+
+ it "matches a set '{<string>,<other>,...}' 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 {<string>,,<other>}" 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 '\\<character>'" 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 '**/<characters>'" 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 = "#<Encoding::Converter: #{source.name} to #{destination.name}>"
+
+ 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 #<Encoding:name> for a non-dummy encoding named 'name'" do
+ Encoding.list.to_a.reject {|e| e.dummy? }.each do |enc|
+ enc.inspect.should =~ /#<Encoding:#{enc.name}>/
+ end
+ end
+
+ it "returns #<Encoding:name (dummy)> for a dummy encoding named 'name'" do
+ Encoding.list.to_a.select {|e| e.dummy? }.each do |enc|
+ enc.inspect.should =~ /#<Encoding:#{enc.name} \(dummy\)>/
+ 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<<i}.should == [1, 2, 'a', 'b']
+
+ end
+
+ it "returns nil when fails(legacy rubycon)" do
+ EnumerableSpecs::EachDefiner.new().send(@method) {|acc,x| 999 }.should == nil
+ end
+end
diff --git a/spec/rubyspec/core/enumerable/shared/take.rb b/spec/rubyspec/core/enumerable/shared/take.rb
new file mode 100644
index 0000000000..bf2536acda
--- /dev/null
+++ b/spec/rubyspec/core/enumerable/shared/take.rb
@@ -0,0 +1,63 @@
+describe :enumerable_take, shared: true do
+ before :each do
+ @values = [4,3,2,1,0,-1]
+ @enum = EnumerableSpecs::Numerous.new(*@values)
+ end
+
+ it "returns the first count elements if given a count" do
+ @enum.send(@method, 2).should == [4, 3]
+ @enum.send(@method, 4).should == [4, 3, 2, 1] # See redmine #1686 !
+ end
+
+ it "returns an empty array when passed count on an empty array" do
+ empty = EnumerableSpecs::Empty.new
+ empty.send(@method, 0).should == []
+ empty.send(@method, 1).should == []
+ empty.send(@method, 2).should == []
+ end
+
+ it "returns an empty array when passed count == 0" do
+ @enum.send(@method, 0).should == []
+ end
+
+ it "returns an array containing the first element when passed count == 1" do
+ @enum.send(@method, 1).should == [4]
+ end
+
+ it "raises an ArgumentError when count is negative" do
+ lambda { @enum.send(@method, -1) }.should raise_error(ArgumentError)
+ end
+
+ it "returns the entire array when count > 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 '#<Exception: Exception>' when no message given" do
+ Exception.new.inspect.should == "#<Exception: Exception>"
+ end
+
+ it "includes #to_s when the result is non-empty" do
+ ExceptionSpecs::OverrideToS.new.inspect.should == "#<ExceptionSpecs::OverrideToS: this is from #to_s>"
+ 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 == "#<ExceptionSpecs::UnExceptional: ExceptionSpecs::UnExceptional>"
+ 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
--- /dev/null
+++ b/spec/rubyspec/core/file/fixtures/do_not_remove
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 = "#<File::Stat dev=0x#{st.dev.to_s(16)}, ino=#{st.ino}, mode=#{sprintf("%07o", st.mode)}, nlink=#{st.nlink}"
+ expected << ", uid=#{st.uid}, gid=#{st.gid}, rdev=0x#{st.rdev.to_s(16)}, size=#{st.size}, blksize=#{st.blksize.inspect}"
+ expected << ", blocks=#{st.blocks.inspect}, atime=#{st.atime}, mtime=#{st.mtime}, ctime=#{st.ctime}"
+ platform_is :bsd, :darwin do
+ # Windows has File.birthtime but it's not here since already shown by ctime.
+ expected << ", birthtime=#{st.birthtime}"
+ end
+ 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 = "<evil voice> 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
--- /dev/null
+++ b/spec/rubyspec/core/io/fixtures/bom_UTF-16BE.txt
Binary files 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
--- /dev/null
+++ b/spec/rubyspec/core/io/fixtures/bom_UTF-16LE.txt
Binary files 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
--- /dev/null
+++ b/spec/rubyspec/core/io/fixtures/bom_UTF-32BE.txt
Binary files 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
--- /dev/null
+++ b/spec/rubyspec/core/io/fixtures/bom_UTF-32LE.txt
Binary files 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
--- /dev/null
+++ b/spec/rubyspec/core/io/fixtures/empty.txt
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 @@
+abcâdef
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("#<Object:0x...>")
+
+ @io.should_receive(:write).with("#<Object:0x...>")
+ @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 =~ /^#<Object:0x[0-9a-f]+>$/
+ 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("%<foo>f", {foo: 1}).should == "1.000000"
+ end
+
+ it "raises KeyError when no matching key is in second argument" do
+ lambda { sprintf("%<foo>f", {}) }.should raise_error(KeyError)
+ end
+
+ it "raises ArgumentError if missing second named argument" do
+ lambda { sprintf("%<key><foo>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
--- /dev/null
+++ b/spec/rubyspec/core/marshal/fixtures/random.dump
Binary files 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>t(?<a>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>t(?<a>ack))/)
+ md['a'].should == 'ack'
+ md['t'].should == 'tack'
+ end
+
+ it "returns the matching version of multiple corresponding named match" do
+ regexp = /(?:
+ A(?<word>\w+)
+ |
+ B(?<word>\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 = /(?<word>hay)(?<word>stack)/.match('haystack')
+ md[:word].should == "stack"
+ md['word'].should == "stack"
+ end
+
+ it "returns nil on non-matching named matches" do
+ regexp = /(?<foo>foo )?(?<bar>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>t(?<a>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>t(?<a>ack))/)
+ lambda { md['baz'] }.should raise_error(IndexError, /baz/)
+ end
+
+ it "returns matches in the String's encoding" do
+ rex = /(?<t>t(?<a>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 == '#<MatchData "HX1138" 1:"H" 2:"X" 3:"113" 4:"8">'
+ 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(/(?<yellow>hay)/)
+ md.names.should be_an_instance_of(Array)
+ end
+
+ it "sets each element to a String" do
+ 'haystack'.match(/(?<yellow>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(/(?<yellow>hay).(?<pin>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>hay)(?<dot>.)(?<hay>tack)/)
+ md.names.should == ['hay', 'dot']
+ end
+
+ it "equals Regexp#names" do
+ r = /(?<hay>hay)(?<dot>.)(?<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
--- /dev/null
+++ b/spec/rubyspec/core/module/fixtures/path2/load_path.rb
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 =~ /#<Module:0x[0-9a-f]+>::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 =~ /#<Module:0x[0-9a-f]+>/
+ end
+
+ it "works with an anonymous class" do
+ c = Class.new
+ c.to_s.should =~ /#<Class:0x[0-9a-f]+>/
+ 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 =~ /^#<Proc:([^ ]*?)(@([^ ]*)\/to_s\.rb:4)?>$/
+ 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 =~ /^#<Proc:([^ ]*?)(@([^ ]*)\/to_s\.rb:10)? \(lambda\)>$/
+ 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 =~ /^#<Proc:([^ ]*?)(@([^ ]*)\/to_s\.rb:16)?>$/
+ 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 =~ /^#<Proc:([^ ]*?)(@([^ ]*)\/to_s\.rb:22)? \(lambda\)>$/
+ 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>is) [aA] (?<pat>pate?rn)/
+ rex.named_captures.keys.should == ['is','pat']
+ end
+
+ it "sets the values of the Hash to Arrays" do
+ rex = /this (?<is>is) [aA] (?<pat>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>is) [aA] (?<pat>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>is) [aA] (?<pat>pate?(?<is>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(?<cap>ee)d(?<ture>le)/.names.each do |name|
+ name.should be_an_instance_of(String)
+ end
+ end
+
+ it "returns all of the named captures" do
+ /n(?<cap>ee)d(?<ture>le)/.names.should == ['cap', 'ture']
+ end
+
+ it "works with nested named captures" do
+ /n(?<cap>eed(?<ture>le))/.names.should == ['cap', 'ture']
+ end
+
+ it "returns each capture name only once" do
+ /n(?<cap>ee)d(?<cap>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 \\<char> 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 \\#<char> 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(/(?<foo>[aeiou])/, '<\k<foo>>').should == "h<e>ll<o>"
+ str.gsub(/(?<foo>.)/, '\k<foo>\k<foo>').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 == "h<e>ll<o>"
+ str.gsub(/(.)/, '\1\1').should == "hheelllloo"
+
+ str.gsub(/.(.?)/, '<\0>(\1)').should == "<he>(e)<ll>(l)<o>()"
+
+ 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 == "<he>llo!"
+ str.gsub("he", '<\&>').should == "<he>llo!"
+ str.gsub("l", '<\0>').should == "he<l><l>o!"
+ str.gsub("l", '<\&>').should == "he<l><l>o!"
+
+ str.gsub(//, '<\0>').should == "<>h<>e<>l<>l<>o<>!<>"
+ str.gsub(//, '<\&>').should == "<>h<>e<>l<>l<>o<>!<>"
+ str.gsub(/../, '<\0>').should == "<he><ll><o!>"
+ str.gsub(/../, '<\&>').should == "<he><ll><o!>"
+ str.gsub(/(.)./, '<\0>').should == "<he><ll><o!>"
+ end
+
+ it "replaces \\` with everything before the current match" do
+ str = "hello!"
+
+ str.gsub("", '<\`>').should == "<>h<h>e<he>l<hel>l<hell>o<hello>!<hello!>"
+ str.gsub("h", '<\`>').should == "<>ello!"
+ str.gsub("l", '<\`>').should == "he<he><hel>o!"
+ str.gsub("!", '<\`>').should == "hello<hello>"
+
+ str.gsub(//, '<\`>').should == "<>h<h>e<he>l<hel>l<hell>o<hello>!<hello!>"
+ str.gsub(/../, '<\`>').should == "<><he><hell>"
+ end
+
+ it "replaces \\' with everything after the current match" do
+ str = "hello!"
+
+ str.gsub("", '<\\\'>').should == "<hello!>h<ello!>e<llo!>l<lo!>l<o!>o<!>!<>"
+ str.gsub("h", '<\\\'>').should == "<ello!>ello!"
+ str.gsub("ll", '<\\\'>').should == "he<o!>o!"
+ str.gsub("!", '<\\\'>').should == "hello<>"
+
+ str.gsub(//, '<\\\'>').should == "<hello!>h<ello!>e<llo!>l<lo!>l<o!>o<!>!<>"
+ str.gsub(/../, '<\\\'>').should == "<llo!><o!><>"
+ 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 == "<h><e><l><l><o><!>"
+
+ 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 == "h<e>ll<o>"
+ str.gsub(/([aeiou])/) { "<#{$1}>" }.should == "h<e>ll<o>"
+ str.gsub("l") { "<#{$~[0]}>" }.should == "he<l><l>o"
+
+ 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
+ "</</h".index("</h").should == 2
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ char = "れ".encode Encoding::EUC_JP
+ lambda do
+ "ã‚ã‚Œ".index char
+ end.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+end
+
+describe "String#index with Regexp" do
+ it "behaves the same as String#index(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.index(regexp).should == str.index(needle)
+
+ 0.upto(str.size + 1) do |start|
+ str.index(regexp, start).should == str.index(needle, start)
+ end
+
+ (-str.size - 1).upto(-1) do |start|
+ str.index(regexp, start).should == str.index(needle, start)
+ end
+ end
+ end
+ end
+
+ it "returns the index of the first match of regexp" do
+ "blablabla".index(/bla/).should == 0
+ "blablabla".index(/BLA/i).should == 0
+
+ "blablabla".index(/.{0}/).should == 0
+ "blablabla".index(/.{6}/).should == 0
+ "blablabla".index(/.{9}/).should == 0
+
+ "blablabla".index(/.*/).should == 0
+ "blablabla".index(/.+/).should == 0
+
+ "blablabla".index(/lab|b/).should == 0
+
+ not_supported_on :opal do
+ "blablabla".index(/\A/).should == 0
+ "blablabla".index(/\Z/).should == 9
+ "blablabla".index(/\z/).should == 9
+ "blablabla\n".index(/\Z/).should == 9
+ "blablabla\n".index(/\z/).should == 10
+ end
+
+ "blablabla".index(/^/).should == 0
+ "\nblablabla".index(/^/).should == 0
+ "b\nablabla".index(/$/).should == 1
+ "bl\nablabla".index(/$/).should == 2
+
+ "blablabla".index(/.l./).should == 0
+ end
+
+ it "sets $~ to MatchData of match and nil when there's none" do
+ 'hello.'.index(/.(.)/)
+ $~[0].should == 'he'
+
+ 'hello.'.index(/not/)
+ $~.should == nil
+ end
+
+ it "starts the search at the given offset" do
+ "blablabla".index(/.{0}/, 5).should == 5
+ "blablabla".index(/.{1}/, 5).should == 5
+ "blablabla".index(/.{2}/, 5).should == 5
+ "blablabla".index(/.{3}/, 5).should == 5
+ "blablabla".index(/.{4}/, 5).should == 5
+
+ "blablabla".index(/.{0}/, 3).should == 3
+ "blablabla".index(/.{1}/, 3).should == 3
+ "blablabla".index(/.{2}/, 3).should == 3
+ "blablabla".index(/.{5}/, 3).should == 3
+ "blablabla".index(/.{6}/, 3).should == 3
+
+ "blablabla".index(/.l./, 0).should == 0
+ "blablabla".index(/.l./, 1).should == 3
+ "blablabla".index(/.l./, 2).should == 3
+ "blablabla".index(/.l./, 3).should == 3
+
+ "xblaxbla".index(/x./, 0).should == 0
+ "xblaxbla".index(/x./, 1).should == 4
+ "xblaxbla".index(/x./, 2).should == 4
+
+ not_supported_on :opal do
+ "blablabla\n".index(/\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.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(/BLA/).should == nil
+
+ "blablabla".index(/.{10}/).should == nil
+ "blaxbla".index(/.x/, 3).should == nil
+ "blaxbla".index(/..x/, 2).should == nil
+ end
+
+ it "returns nil if the Regexp matches the empty string and the offset is out of range" do
+ "ruby".index(//,12).should be_nil
+ end
+
+ it "supports \\G which matches at the given start offset" do
+ "helloYOU.".index(/\GYOU/, 5).should == 5
+ "helloYOU.".index(/\GYOU/).should == nil
+
+ re = /\G.+YOU/
+ # The # marks where \G will match.
+ [
+ ["#hi!YOUall.", 0],
+ ["h#i!YOUall.", 1],
+ ["hi#!YOUall.", 2],
+ ["hi!#YOUall.", nil]
+ ].each do |spec|
+
+ start = spec[0].index("#")
+ str = spec[0].delete("#")
+
+ str.index(re, start).should == spec[1]
+ end
+ end
+
+ it "converts start_offset to an integer via to_int" do
+ obj = mock('1')
+ obj.should_receive(:to_int).and_return(1)
+ "RWOARW".index(/R./, obj).should == 4
+ 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 "treats the offset as a character index" do
+ "ã‚ã‚Œã‚ã‚ã‚Œ".index(/ã‚/, 3).should == 3
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ re = Regexp.new "れ".encode(Encoding::EUC_JP)
+ lambda do
+ "ã‚ã‚Œ".index re
+ end.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+end
diff --git a/spec/rubyspec/core/string/initialize_spec.rb b/spec/rubyspec/core/string/initialize_spec.rb
new file mode 100644
index 0000000000..cbb281c8d5
--- /dev/null
+++ b/spec/rubyspec/core/string/initialize_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/replace', __FILE__)
+
+describe "String#initialize" do
+ it "is a private method" do
+ String.should have_private_instance_method(:initialize)
+ end
+
+ describe "with no arguments" do
+ it "does not change self" do
+ s = "some string"
+ s.send :initialize
+ s.should == "some string"
+ end
+
+ it "does not raise an exception when frozen" do
+ a = "hello".freeze
+ a.send(:initialize).should equal(a)
+ end
+ end
+
+ describe "with an argument" do
+ it_behaves_like :string_replace, :initialize
+ end
+end
diff --git a/spec/rubyspec/core/string/insert_spec.rb b/spec/rubyspec/core/string/insert_spec.rb
new file mode 100644
index 0000000000..c207fcc13b
--- /dev/null
+++ b/spec/rubyspec/core/string/insert_spec.rb
@@ -0,0 +1,84 @@
+# -*- encoding: utf-8 -*-
+
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../fixtures/classes.rb', __FILE__)
+
+describe "String#insert with index, other" do
+ it "inserts other before the character at the given index" do
+ "abcd".insert(0, 'X').should == "Xabcd"
+ "abcd".insert(3, 'X').should == "abcXd"
+ "abcd".insert(4, 'X').should == "abcdX"
+ end
+
+ it "modifies self in place" do
+ a = "abcd"
+ a.insert(4, 'X').should == "abcdX"
+ a.should == "abcdX"
+ end
+
+ it "inserts after the given character on an negative count" do
+ "abcd".insert(-5, 'X').should == "Xabcd"
+ "abcd".insert(-3, 'X').should == "abXcd"
+ "abcd".insert(-1, 'X').should == "abcdX"
+ end
+
+ it "raises an IndexError if the index is beyond string" do
+ lambda { "abcd".insert(5, 'X') }.should raise_error(IndexError)
+ lambda { "abcd".insert(-6, 'X') }.should raise_error(IndexError)
+ end
+
+ it "converts index to an integer using to_int" do
+ other = mock('-3')
+ other.should_receive(:to_int).and_return(-3)
+
+ "abcd".insert(other, "XYZ").should == "abXYZcd"
+ end
+
+ it "converts other to a string using to_str" do
+ other = mock('XYZ')
+ other.should_receive(:to_str).and_return("XYZ")
+
+ "abcd".insert(-3, other).should == "abXYZcd"
+ end
+
+ it "taints self if string to insert is tainted" do
+ str = "abcd"
+ str.insert(0, "T".taint).tainted?.should == true
+
+ str = "abcd"
+ other = mock('T')
+ def other.to_str() "T".taint end
+ str.insert(0, other).tainted?.should == true
+ end
+
+ it "raises a TypeError if other can't be converted to string" do
+ lambda { "abcd".insert(-6, Object.new)}.should raise_error(TypeError)
+ lambda { "abcd".insert(-6, []) }.should raise_error(TypeError)
+ lambda { "abcd".insert(-6, mock('x')) }.should raise_error(TypeError)
+ end
+
+ it "raises a RuntimeError if self is frozen" do
+ str = "abcd".freeze
+ lambda { str.insert(4, '') }.should raise_error(RuntimeError)
+ lambda { str.insert(4, 'X') }.should raise_error(RuntimeError)
+ end
+
+ with_feature :encoding do
+ it "inserts a character into a multibyte encoded string" do
+ "ã‚ã‚ŠãŒã¨ã†".insert(1, 'ü').should == "ã‚üりãŒã¨ã†"
+ end
+
+ it "returns a String in the compatible encoding" do
+ str = "".force_encoding(Encoding::US_ASCII)
+ str.insert(0, "ã‚ã‚ŠãŒã¨ã†")
+ str.encoding.should == Encoding::UTF_8
+ end
+
+ it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
+ pat = "ã‚¢".encode Encoding::EUC_JP
+ lambda do
+ "ã‚ã‚Œ".insert 0, pat
+ end.should raise_error(Encoding::CompatibilityError)
+ end
+ end
+end
diff --git a/spec/rubyspec/core/string/inspect_spec.rb b/spec/rubyspec/core/string/inspect_spec.rb
new file mode 100644
index 0000000000..a3e18c0ee3
--- /dev/null
+++ b/spec/rubyspec/core/string/inspect_spec.rb
@@ -0,0 +1,492 @@
+# -*- encoding: utf-8 -*-
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../fixtures/classes.rb', __FILE__)
+
+describe "String#inspect" do
+ it "taints the result if self is tainted" do
+ "foo".taint.inspect.tainted?.should == true
+ "foo\n".taint.inspect.tainted?.should == true
+ end
+
+ it "untrusts the result if self is untrusted" do
+ "foo".untrust.inspect.untrusted?.should == true
+ "foo\n".untrust.inspect.untrusted?.should == true
+ end
+
+ it "does not return a subclass instance" do
+ StringSpecs::MyString.new.inspect.should be_an_instance_of(String)
+ end
+
+ it "returns a string with special characters replaced with \\<char> 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 \\#<char> 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
+ ("%<foo>d" % {foo: 1}).should == "1"
+ end
+
+ it "raises KeyError if key is missing from passed-in hash" do
+ lambda {"%<foo>d" % {}}.should raise_error(KeyError)
+ end
+
+ it "should raise ArgumentError if no hash given" do
+ lambda {"%<foo>" % []}.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<e380>"
+ 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 '&amp;'" do
+ '& and &'.send(@method, "UTF-8", xml: :text).should == '&amp; and &amp;'
+ end
+
+ it "replaces all instances of '<' with '&lt;'" do
+ '< and <'.send(@method, "UTF-8", xml: :text).should == '&lt; and &lt;'
+ end
+
+ it "replaces all instances of '>' with '&gt;'" do
+ '> and >'.send(@method, "UTF-8", xml: :text).should == '&gt; and &gt;'
+ 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 == '&#xFC;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 '&amp;'" do
+ '& and &'.send(@method, "UTF-8", xml: :attr).should == '"&amp; and &amp;"'
+ end
+
+ it "replaces all instances of '<' with '&lt;'" do
+ '< and <'.send(@method, "UTF-8", xml: :attr).should == '"&lt; and &lt;"'
+ end
+
+ it "replaces all instances of '>' with '&gt;'" do
+ '> and >'.send(@method, "UTF-8", xml: :attr).should == '"&gt; and &gt;"'
+ end
+
+ it "replaces all instances of '\"' with '&quot;'" do
+ '" and "'.send(@method, "UTF-8", xml: :attr).should == '"&quot; and &quot;"'
+ end
+
+ it "replaces undefined characters with their upper-case hexadecimal numeric character references" do
+ 'ürst'.send(@method, Encoding::US_ASCII, xml: :attr).should == '"&#xFC;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, /(?<g>[aeiou](.))/, 'g').should == "el"
+ "hello there".send(@method, /[aeiou](?<g>.)/, 'g').should == "l"
+
+ "har".send(@method, /(?<g>(.)(.)(.))/, 'g').should == "har"
+ "har".send(@method, /(?<h>.)(.)(.)/, 'h').should == "h"
+ "har".send(@method, /(.)(?<a>.)(.)/, 'a').should == "a"
+ "har".send(@method, /(.)(.)(?<r>.)/, 'r').should == "r"
+ "har".send(@method, /(?<h>.)(?<a>.)(?<r>.)/, 'r').should == "r"
+ end
+
+ it "returns the last capture for duplicate names" do
+ "hello there".send(@method, /(?<g>h)(?<g>.)/, 'g').should == "e"
+ "hello there".send(@method, /(?<g>h)(?<g>.)(?<f>.)/, 'g').should == "e"
+ end
+
+ it "returns the innermost capture for nested duplicate names" do
+ "hello there".send(@method, /(?<g>h(?<g>.))/, '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, /(?<hi>hello)/, 'hi').tainted?.should == str.tainted?
+
+ str.send(@method, /(?<g>(.)(.)(.))/, 'g').tainted?.should == str.tainted?
+ str.send(@method, /(?<h>.)(.)(.)/, 'h').tainted?.should == str.tainted?
+ str.send(@method, /(.)(?<a>.)(.)/, 'a').tainted?.should == str.tainted?
+ str.send(@method, /(.)(.)(?<r>.)/, 'r').tainted?.should == str.tainted?
+ str.send(@method, /(?<h>.)(?<a>.)(?<r>.)/, 'r').tainted?.should == str.tainted?
+
+ tainted_re = /(?<a>.)(?<b>.)(?<c>.)/
+ 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, /(?<whut>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, /(?<q>.)/, mock('x')) }.should raise_error(TypeError)
+ lambda { "hello".send(@method, /(?<q>.)/, {}) }.should raise_error(TypeError)
+ lambda { "hello".send(@method, /(?<q>.)/, []) }.should raise_error(TypeError)
+ end
+
+ it "raises an IndexError when given the empty String as a group name" do
+ lambda { "hello".send(@method, /(?<q>)/, '') }.should raise_error(IndexError)
+ end
+
+ it "returns subclass instances" do
+ s = StringSpecs::MyString.new("hello")
+ s.send(@method, /(?<q>.)/, '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>.(.))/, 'hi')
+ $~[0].should == 'he'
+
+ 'hello'.send(@method, /(?<non>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"
+
+ "<<koala>>".send(@method).should == "<<koalb>>"
+ "==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 = "<<subbookkeeper!!!>>"
+ s.squeeze("beko", "^e").should == s.squeeze("bko")
+ s.squeeze("^<bek!>").should == s.squeeze("o")
+ s.squeeze("^o").should == s.squeeze("<bek!>")
+ 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 == "h<e>llo"
+ str.sub(/(.)/, '\1\1').should == "hhello"
+
+ str.sub(/.(.?)/, '<\0>(\1)').should == "<he>(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 == "<he>llo!"
+ str.sub("he", '<\&>').should == "<he>llo!"
+ str.sub("l", '<\0>').should == "he<l>lo!"
+ str.sub("l", '<\&>').should == "he<l>lo!"
+
+ str.sub(//, '<\0>').should == "<>hello!"
+ str.sub(//, '<\&>').should == "<>hello!"
+ str.sub(/../, '<\0>').should == "<he>llo!"
+ str.sub(/../, '<\&>').should == "<he>llo!"
+ str.sub(/(.)./, '<\0>').should == "<he>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 == "he<he>lo!"
+ str.sub("!", '<\`>').should == "hello<hello>"
+
+ str.sub(//, '<\`>').should == "<>hello!"
+ str.sub(/..o/, '<\`>').should == "he<he>!"
+ end
+
+ it "replaces \\' with everything after the current match" do
+ str = "hello!"
+
+ str.sub("", '<\\\'>').should == "<hello!>hello!"
+ str.sub("h", '<\\\'>').should == "<ello!>ello!"
+ str.sub("ll", '<\\\'>').should == "he<o!>o!"
+ str.sub("!", '<\\\'>').should == "hello<>"
+
+ str.sub(//, '<\\\'>').should == "<hello!>hello!"
+ str.sub(/../, '<\\\'>').should == "<llo!>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 == "<h>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 == "h<e>llo"
+ str.sub(/([aeiou])/) { "<#{$1}>" }.should == "h<e>llo"
+ str.sub("l") { "<#{$~[0]}>" }.should == "he<l>lo"
+
+ 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 == "h<e>llo"
+ str.dup.sub!(/([aeiou])/) { "<#{$1}>" }.should == "h<e>llo"
+ str.dup.sub!("l") { "<#{$~[0]}>" }.should == "he<l>lo"
+
+ 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<!'
+ 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 '>'" 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<!'
+ 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 '>'" 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<!'
+ 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_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<!'
+ 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_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<!'
+ 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_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<!'
+ 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_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<!'
+ 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_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<!'
+ 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_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<!'
+ 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_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<!'
+ 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_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<!'
+ 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_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<!'
+ 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_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<!'
+ 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 '>'" 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<!'
+ 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 '>'" 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<X.0``\n", ["0123456789"]],
+ ["'.CL\\/3X_0```\n", [":;<=>?@"]],
+ [":04)#1$5&1TA)2DM,34Y/4%%24U155E=865H`\n", ["ABCDEFGHIJKLMNOPQRSTUVWXYZ"]],
+ ["&6UQ=7E]@\n", ["[\\]^_`"]],
+ [":86)C9&5F9VAI:FML;6YO<'%R<W1U=G=X>7H`\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 == '#<struct StructClasses::Car make="Ford", model="Ranger", year=nil>'
+ # 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 == '#<struct Whiskey name="Jack", ounces=100>'
+ 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 == '#<struct a="">'
+ 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
+ :"<<koala>>".send(@method).should == :"<<koalb>>"
+ :"***".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 == '<top (required)>'
+ 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('<top (required)>')
+ 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